2015-06-10 13 views
12

sytuacja:

Chcę utworzyć funkcję mysql o nazwie XMLify, które odbywają się w ciąg znaków i wyrażenia, które zwraca zestawfunkcja MySQL UDF aby powrócić XML

XMLify(string, expr)

Funkcja powinna zawiń każde zwrócone pole każdego zwróconego wiersza w zestawie do własnego znacznika XML. Nazwa tagu powinna być nazwą pola.

Mały przykład:

select XMLify('foo', (SELECT 1 as `a`, 2 as `b` UNION SELECT 3 as `a`, 4 as `b`)); 

powinien zwrócić:

<foo><a>1</a><b>2</b></foo><foo><a>3</a><b>4</b></foo> 

Chcę mieć to, ponieważ umożliwi mi uruchomić kompleksową frazę wiele łączy i/lub podzapytania zależnych, bez konieczności zwracania nadmiarowych danych do klienta.

Mam już obejście bez funkcji, którą chcę zbudować. Ale wymaga to pisania trudnych pytań, które nie są łatwe do utrzymania. Zobacz moje przykłady poniżej.

Upewnienie się, że nazwy pól są poprawne Nazwa węzła XML jest dla późniejszego problemu. Gdy funkcja stanie, pomyślę o jakimś algorytmie, który zajmie nazwę pola i przekształci go w legalną nazwę węzła XML.

Również ucieczka danych XML jest dla późniejszego niepokoju. Zostanie to wykonane przy użyciu innej funkcji o nazwie CDATAify, która po prostu zapisze wszystkie dane w <![CDATA[ i ]]> i spowoduje uniknięcie wszelkich wcześniejszych wystąpień ]]> w danych w .

Nie udało mi się tego dokonać przy użyciu zapisanych funkcji w MySQL, ponieważ te nie biorą w zestawach wyników. Ponadto, nawet jeśli miałbyś przekazać SQL jako ciąg, a następnie przygotować instrukcję i wykonać ją, nie możesz uzyskać dostępu do pól, jeśli jeszcze nie znasz nazw pól.

Więc teraz zastanawiam się, czy sztuczka może zostać wykonana za pomocą funkcji zdefiniowanych przez użytkownika (UDF). To jest coś, z czym jeszcze nie współpracowałem, i chciałbym, żeby pani poradziła tutaj przed zawikłaniem.

PYTANIA:

Więc moje pytania teraz to:

  • Podsumowując, chciałbym mieć funkcję MySQL, że mogę przekazać wyrażenie lub wynikowej ustawić i gdzie mogę również użyć nazwy pól zestawu wyników.
  • Czy zakładam, że nie będzie to możliwe w przechowywanych funkcjach?
  • Czy UDF będzie wykorzystywał expessions/ich zestaw wyników jako argument?
  • Czy funkcja UDF zezwala na dostęp do nazw pól zestawu wyników, więc mogę używać ich jako nazw znaczników XML?
  • Czy to działa również na Windowsie? Czytałem, że UDF ma pewne ograniczenia.
  • Czy istnieje lepszy sposób, o którym jeszcze nie pomyślałem?
  • Czy będę w stanie utworzyć plik UDF .dll, który mogę utworzyć na własnym komputerze programistycznym, a następnie skopiować plik .dll na mój serwer i użyć go tam?
  • Jak uzyskać ten program na rolce? Proszę być dokładnym i wziąć pod uwagę, że mam 64-bitowy MySQL 5.5 na komputerze z systemem Windows.

PRZYKŁAD:

Wyobraźmy sobie, że przez 3 następujące tabele:

users:   grades:    toys: 
+----+------+ +--------+-------+ +--------+--------------+ 
| id | name | | userid | grade | | userid | toy   | 
+----+------+ +--------+-------+ +--------+--------------+ 
| 1 | Bart | |  1 |  E | |  1 | slingshot | 
| 2 | Lisa | |  1 |  E | |  1 | Krusty  | 
| .. | ... | |  2 |  A | |  2 | Malibu Stacy | 
| .. | ... | |  2 |  B | |  2 | calculator | 
+----+------+ +--------+-------+ +--------+--------------+ 

My pożądany wynik byłoby ograniczone do Barta i Lisa:

<users> 
    <user> 
     <id><![CDATA[1]]></id> 
     <name><![CDATA[Bart]]></name> 
     <grades> 
      <grade><![CDATA[E]]></grade> 
      <grade><![CDATA[E]]></grade> 
     </grades> 
     <toys> 
      <toy><![CDATA[slingshot]]></toy> 
      <toy><![CDATA[Krusty]]></toy> 
     </toys> 
    </user> 
    <user> 
     <id><![CDATA[1]]></id> 
     <name><![CDATA[Lisa]]></name> 
     <grades> 
      <grade><![CDATA[A]]></grade> 
      <grade><![CDATA[B]]></grade> 
     </grades> 
     <toys> 
      <toy><![CDATA[Malibu Stacey]]></toy> 
      <toy><![CDATA[calculator]]></toy> 
     </toys> 
    </user> 
</users> 

Rozpatrzenie:

  • Nie chcę w PHP lub C#, aby najpierw zapytać o tabelę użytkowników, a następnie dla każdego użytkownika uruchomić dwa dodatkowe zapytania dotyczące ocen i zabawek. Ponieważ dla 1000 użytkowników, byłbym uruchomiony 2001 kwerendy.
  • Nie chcę również uruchamiać zapytania ze wszystkimi złączeniami i przechodzić przez zestaw wyników w PHP lub C#, ponieważ nazwa użytkownika byłaby wysyłana tyle razy, ile razy liczba razy razy liczba zabawek. Wyobraź sobie, że masz pole użytkownika zawierające ogromną kroplę!
  • Nie mogę po prostu użyć GROUP_CONCAT na połączonych tabelach, ponieważ oceny/zabawki nadal wyglądałyby podwójnie.
  • Jeśli użyłbym GROUP_CONCAT z DISTINCT, stracę oceny z tymi samymi, takimi jak dwa Ery Barta.

Obecnie chciałbym użyć poniższej instrukcji, aby uzyskać ten wynik, z udziałem dwóch zależnych podzapytań. Działa to doskonale:

SELECT 
    CONCAT(
     '<users>', 
      IFNULL(
       GROUP_CONCAT(
        '<user>', 
         '<id><![CDATA[', 
          REPLACE(u.id,']]>',']]]]><![CDATA[>'), 
         ']]></id>', 
         '<name><![CDATA[', 
          REPLACE(u.name,']]>',']]]]><![CDATA[>'), 
         ']]></name>', 
         '<grades>', 
          (
           SELECT 
            IFNULL(
             GROUP_CONCAT(
              '<grade><![CDATA[', 
               REPLACE(g.grade,']]>',']]]]><![CDATA[>'), 
              ']]></grade>' 
              SEPARATOR '' 
             ), 
            '') 
           FROM 
            grades g 
           WHERE 
            g.userid = u.id 
          ), 
         '</grades>', 
         '<toys>', 
          (
           SELECT 
            IFNULL(
             GROUP_CONCAT(
              '<toys><![CDATA[', 
               REPLACE(t.toy,']]>',']]]]><![CDATA[>'), 
              ']]></toys>' 
              SEPARATOR '' 
             ), 
            '') 
           FROM 
            toys t 
           WHERE 
            t.userid = u.id 
          ), 
         '</toys>', 
        '</user>' 
        SEPARATOR '' 
       ), 
       '' 
      ), 
     '</users>' 
    ) 
FROM 
    users u 
WHERE 
    u.name = 'Bart' or u.name = 'Lisa' 
; 

Teraz, jak można zauważyć, że jest to dość duży i brzydki kwerendy, która rani oczu podczas czytania. Utrzymanie takiego zapytania jest trudne. Jeśli musiałbym Moje funkcje XMLify i CDATAify, może po prostu napisać to zamiast:

SELECT 
    XMLify('users',(
     XMLify('user',(
      SELECT 
       CDATAify(u.id) as id, 
       CDATAify(u.name) as name, 
       XMLify('grade',(
        SELECT 
         CDATAify(g.grade) as grade 
        FROM 
         grades g 
        where 
         g.userid = u.id 
       )) AS grades, 
       XMLify('toys',(
        SELECT 
         CDATAify(t.toy) as toy 
        FROM 
         toys t 
        where 
         t.userid = u.id 
       )) AS grades 
      FROM 
       users u 
      WHERE 
       u.name = 'Bart' or u.name = 'Lisa' 
     )) 
    )) 
; 

EDIT:

Jak wspomniano w komentarzach przez nb, istnieje repository on Github, ewentualnie posiadający wszystko czego potrzebuję. Jednak spędziłem kilka dni, próbując uruchomić to w moim systemie, bez powodzenia. Każda odpowiedź, która zawiera szczegółowe instrukcje instalacji na 64-bitowym serwerze MySQL 5.5 działającym w systemie Windows, jest również akceptowalna.

Proszę wziąć pod uwagę, że nie mam żadnego doświadczenia z markami, plikami makefiles itp. Proszę więc dokładnie wyjaśnić.

+0

Cóż, wydaje się, [będzie to dokładnie to, co chcesz] (https://github.com/mysqludf/lib_mysqludf_xml#readme). To wątpliwe, czy wybrałeś właściwą ścieżkę, widząc, że jedyną rzeczą, której brakuje, są nazwy pól. –

+0

@ N.B. Spędziłem kilka dni, aby to zadziałało, ale ciągle mi się nie udaje. Próbowałem skopiować plik lib_mysqludf_xql.dll, dostarczony w folderze "win" bezpośrednio do mojego serwera MySQL 5.5/lib/plugin i uruchamiając plik installdb.sql, ale nadal otrzymuję komunikat 'ERROR 1126 (HY000): Nie można otworzyć udostępnionego biblioteka 'lib_mysqludf_xql.dll' (errno: 193) '. Chciałem też skompilować własną bibliotekę DLL, ale bez względu na to, co robię, ciągle mi się nie udaje. Próbowałem używać Windows SDK, CMake, Cygwin, ale wydaje się, że po prostu nie wiem jak stworzyć własny .dll. Mam MySQL 5.5 64 bit w systemie Windows, tak jak na moim serwerze. To się nie zmieni.) –

+0

[Sprawdź ten link, sekcja rozwiązywania problemów] (https://github.com/dtrebbien/lib_mysqludf_str/blob/master/README.win_x64.txt) - Nigdy nie używał MySQL w Windows, więc nie mam dużo pomoc tam, ale może być, że albo masz błędną dystrybucję C++ lub MySQL ma inny katalog wtyczek. 'POKAŻ ZMIENNY JAK"% plugin% ";' powinien pokazywać, gdzie próbuje załadować wtyczki. Poza tym, nie wiem, o ile więcej mogę pomóc:/ –

Odpowiedz

1

x, właśnie dzisiaj znalazłem to pytanie, mam tylko nadzieję, że odpowiem na to pytanie nie (zbyt) późno, a jeśli będzie za późno, może to pomoże komuś innemu.

Przyczyna MySql nie zezwala na zaimplementowanie zapytań dynamiki na temat funkcji lub wyzwalaczy Po prostu wybiorę zaimplementowanie procedury składowanej.

DELIMITER // 
DROP PROCEDURE IF EXISTS XMLify// 
CREATE PROCEDURE XMLify(IN wraper VARCHAR(100), IN expr VARCHAR(1000)) 
LANGUAGE SQL NOT DETERMINISTIC READS SQL DATA SQL SECURITY INVOKER 
BEGIN 
    DECLARE done INT DEFAULT FALSE; 
    DECLARE col_name VARCHAR(255); 
    DECLARE cur1 CURSOR FOR 
    SELECT 
     column_name 
    FROM 
     information_schema.columns 
    WHERE 
     table_schema = 'test' AND /*Name of the database (schema)*/ 
     table_name = 'temp' AND 
     column_name <> 'c4l5mn'; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 

    DROP TABLE IF EXISTS temp; 
    SET @SQL = CONCAT('CREATE TABLE temp (c4l5mn TINYINT NOT NULL DEFAULT ''1'') AS ', expr); 
    PREPARE stmt1 FROM @SQL; 
    EXECUTE stmt1; 
    DEALLOCATE PREPARE stmt1; 

    OPEN cur1; 
    SET col_name = ''; 
    SET @SQL = ''; 
    read_loop: LOOP 
     FETCH cur1 INTO col_name; 
     IF done THEN 
      LEAVE read_loop; 
     END IF; 
     SET @SQL = CONCAT(@SQL, '<', col_name, '>'', ', col_name, ', ''</', col_name, '>'); 
    END LOOP; 
    CLOSE cur1; 
    SET @SQl = CONCAT('SELECT GROUP_CONCAT(CONCAT(''<', wraper, '>', @SQL, '</', wraper, '>'') SEPARATOR '''') row FROM temp GROUP BY c4l5mn'); 
    PREPARE stmt1 FROM @SQL; 
    EXECUTE stmt1; 
    DEALLOCATE PREPARE stmt1; 
    DROP TABLE IF EXISTS temp; 
END// 
DELIMITER ; 

to wszystko, teraz można nazwać jak

CALL XMLify('foo', 'SELECT 1 as `a`, 2 as `b` UNION SELECT 3, 4'); 

I powróci

<foo><a>1</a><b>2</b></foo><foo><a>3</a><b>4</b></foo> 

połączeń

CALL XMLify('foo', 'SELECT 1 as a, 2 as b, 3 as c UNION SELECT 4, 5, 6'); 

Wrócimy

<foo><a>1</a><b>2</b><c>3</c></foo><foo><a>4</a><b>5</b><c>6</c></foo> 

Mam tylko nadzieję, że to pomoże Pozdrowienia

Powiązane problemy