2015-06-10 16 views
12

Situazione:

Voglio creare una funzione mysql denominata XMLify, che prende in una stringa e un'espressione che restituirà un insiemefunzione di MySQL UDF per tornare XML

XMLify(string, expr)

La funzione dovrebbe avvolgere ogni campo restituito di ogni riga restituita nel set, nel proprio tag XML. Il nome del tag dovrebbe essere il nome del campo.

piccolo esempio:

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

deve restituire:

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

voglio avere questo, perché mi permetterà di eseguire una query complessa con molte giunture e o sottoquery/dipendenti, senza dover restituire dati ridondanti al client.

Ho già un work-around senza la funzione che voglio costruire. Ma questo comporta la scrittura di domande difficili che non sono facilmente mantenute. Vedere i miei esempi qui sotto.

Assicurarsi che i nomi dei campi siano legali Il nome del nodo XML è per una preoccupazione successiva. Una volta che la funzione è valida, penserò ad un algoritmo che prenderà il nome del campo e lo trasformerà in un nome di nodo XML legale.

Anche l'escape dei dati XML è per una preoccupazione successiva. Questa operazione verrà eseguita con una diversa funzione denominata , che includerà semplicemente tutti i dati in <![CDATA[ e ]]> e eviterà qualsiasi occorrenza precedente di ]]> nei dati in ]]]]><![CDATA[>.

Non sono stato in grado di farlo utilizzando le funzioni memorizzate in MySQL, perché non contengono i set di risultati. Inoltre, anche se si dovesse passare SQL come stringa e quindi preparare la dichiarazione ed eseguirla, non è possibile accedere ai campi se non si conoscono già i nomi dei campi.

Così ora mi chiedo se il trucco può essere fatto con le funzioni definite dall'utente (UDF). Questo è qualcosa con cui non ho ancora lavorato, e mi piacerebbe il vostro consiglio qui prima di imbarcare.

DOMANDE:

Quindi le mie domande ora sono:

  • Per ricapitolare, mi piacerebbe avere una funzione di MySQL che posso passare un'espressione o set di risultati, e dove posso anche usare il nomi dei campi del set di risultati.
  • Sto assumendo correttamente che questo non sarà possibile nelle funzioni memorizzate?
  • UDF prenderà in expessions/il loro risultato impostato come argomento?
  • UDF consentirà di accedere ai nomi dei campi del set di risultati, quindi posso usarli come nomi dei tag XML
  • Funzionerà anche su Windows? Ho letto che UDF ha alcune limitazioni
  • C'è un modo migliore a cui non ho ancora pensato?
  • Potrò avere una .dll UDF che posso creare sul mio computer di sviluppo e quindi copiare il file .dll sul mio server e usarlo lì?
  • Come si ottiene questo spettacolo su un rotolo? Si prega di essere accurati e tenere conto che ho MySQL 5.5 a 64 bit su un computer Windows.

ESEMPIO:

Immagini avere i seguenti 3 tabelle:

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 | 
+----+------+ +--------+-------+ +--------+--------------+ 

mio risultato desiderato sarebbe, limitato a Bart e 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> 

Esame:

  • Non voglio in PHP o C# per prima cosa interrogare la tabella utente, e quindi per utente eseguire due query aggiuntive per i gradi e i giocattoli. Perché per 1000 utenti, avrei eseguito query 2001.
  • Inoltre, non voglio eseguire una query con tutti i join e passare attraverso il set di risultati in PHP o C#, poiché il nome utente verrebbe inviato tante volte quante il numero di voti moltiplicato per il numero di giocattoli. Immagina di avere un campo utente contenente un enorme blob!
  • Non posso semplicemente utilizzare GROUP_CONCAT sulle tabelle unite, poiché i gradi/giocattoli continuerebbero ad apparire doppi.
  • E se usassi GROUP_CONCAT con DISTINCT, perderò i voti con lo stesso, come i due di Bart.

Così attualmente vorrei usare la seguente dichiarazione per ottenere questo risultato, coinvolgendo due subquery dipendenti. Questa grande opera:

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' 
; 

Ora, come si può notare, si tratta di una piuttosto grande e brutto query, che fa male agli occhi durante la lettura. Mantenere una simile richiesta è difficile. Se avessi le mie funzioni XMLify e CDATAify, avrei potuto semplicemente scrivere questo invece:

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:

Come accennato nei commenti di N.B., c'è un repository on Github, possibilmente tenendo tutto quello che serve. Tuttavia, ho trascorso diversi giorni cercando di far funzionare tutto questo sul mio sistema, senza successo. È accettabile anche qualsiasi risposta che spieghi passo passo come installare questo sul mio server MySQL 5.5 a 64 bit in esecuzione su Windows.

Si prega di prendere in considerazione che non ho esperienza con marche, makefile, ecc. Quindi, per favore sii accurato nello spiegare.

+0

Beh, sembra [questo sarebbe esattamente quello che vuoi] (https://github.com/mysqludf/lib_mysqludf_xml#readme). È discutibile che tu abbia scelto la strada giusta, visto che l'unica cosa che ti manca sono i nomi dei campi. –

+0

@ N.B. Ho passato diversi giorni a farlo funzionare, ma continuo a fallire. Ho provato a copiare lib_mysqludf_xql.dll come fornito nella cartella 'win' direttamente nel mio 'MySQL Server 5.5/lib/plugin' ed eseguire l'installdb.sql, ma continuo a ricevere 'ERROR 1126 (HY000): Impossibile aprire condiviso libreria 'lib_mysqludf_xql.dll' (errno: 193) '. Volevo anche compilare la mia DLL, ma non importa quello che faccio, continuo a fallire. Ho provato a utilizzare Windows SDK, CMake, Cygwin, ma sembra proprio che non so come creare il mio .dll. Ho MySQL 5.5 a 64 bit su Windows, proprio come sul mio server. Questo non può cambiare.) –

+0

[Controlla questo collegamento, risoluzione dei problemi di sezione] (https://github.com/dtrebbien/lib_mysqludf_str/blob/master/README.win_x64.txt) - Non ho mai usato MySQL su Windows, quindi non sono di un grande aiuto, ma potrebbe darsi che tu abbia una distribuzione C++ sbagliata o che MySQL abbia una diversa directory di plugin. 'MOSTRA VARIABILI COME '% plugin%';' dovrebbe mostrare da dove tenta di caricare i plugin. Oltre a questo, non so quanto più posso essere d'aiuto:/ –

risposta

1

x, proprio oggi ho trovato questa domanda, spero solo di rispondere a questa domanda non (troppo) tardi e se è troppo tardi, forse aiuterà qualcun altro.

Causa MySql non consente di implementare query dinamiche su funzioni o trigger. Ho solo scelto di implementare una stored procedure.

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 ; 

il gioco è fatto, ora si può chiamare proprio come

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

e tornerà

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

chiamata

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

tornerà

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

Spero solo che aiuterebbe Saluti