Come posso creare una funzione aggregata definita dall'utente?


8

Ho bisogno di una funzione aggregata che MySQL non fornisce.

Vorrei che fosse nel sapore di SQL di MySQL (cioè non in C).

Come faccio a fare questo? Ciò su cui sono bloccato è la creazione di una funzione aggregata: i documenti non sembrano menzionare il modo in cui ciò viene fatto.

Esempi di utilizzo desiderato di una productfunzione:

mysql> select product(col) as a from `table`;
+------+
| a    |
+------+
|  144 |
+------+
1 row in set (0.00 sec)

mysql> select col, product(col) as a from `table` group by col;
+-----+------+
| col | a    |
+-----+------+
|   6 |   36 |
|   4 |    4 |
+-----+------+
2 rows in set (0.01 sec)

Risposte:


7

Secondo la documentazione http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html è possibile scrivere solo funzioni aggregate in C. Siamo spiacenti!


O C o C ++. Non SQL, comunque.
— Mike Sherrill "Cat Recall",

1
Presumo qualsiasi linguaggio in grado di generare librerie binarie nel formato binario supportato dalla piattaforma con convenzioni di chiamata C.
— Colin 't Hart,

Non lo so. È stato documentato come "C o C ++ (o un altro linguaggio che può usare le convenzioni di chiamata C)" nella versione 5.0. I documenti rilasciati "o un'altra lingua che può usare le convenzioni di chiamata C" nella versione 5.1. È una frase strana da abbandonare.
— Mike Sherrill 'Cat Recall',

è ora disponibile nelle recenti versioni di mysql (dopo qualche anno)?
— Dinesh,

9

Non so se esiste un modo per definire una nuova funzione aggregata, non senza fare confusione con il codice sorgente MySQL.

Ma se i tuoi numeri sono tutti positivi, potresti benissimo derivare dall'identità aritmetica:

log( product( Ai ) ) = sum( log( Ai ) )

che puoi usare EXP(SUM(LOG(x)))per calcolare PRODUCT(x). Test in SQL-Fiddle :

SELECT EXP(SUM(LOG(a))) AS product
FROM t ;

SELECT col, EXP(SUM(LOG(a))) AS product
FROM t 
GROUP BY col ;

Quando i dati possono avere 0, diventa un po 'più complicato:

SELECT (NOT EXISTS (SELECT 1 FROM t WHERE a = 0)) 
       * EXP(SUM(LOG(a))) AS p
FROM t 
WHERE a > 0 ;

SELECT d.col, 
       (NOT EXISTS (SELECT 1 FROM t AS ti WHERE ti.col = d.col AND ti.a = 0)) 
       * COALESCE(EXP(SUM(LOG(t.a))),1)  AS p
FROM 
    ( SELECT DISTINCT col
      FROM t
    ) AS d
  LEFT JOIN
    t  ON  t.col = d.col
       AND t.a > 0
GROUP BY d.col ;

Testato su SQL-Fiddle


Per altri DBMS, che non hanno la conversione automatica di MySQL di valori booleani in numeri interi, il

(NOT EXISTS (SELECT ...))

dovrebbe essere sostituito con:

(CASE WHEN EXISTS (SELECT 1...) THEN 0 ELSE 1 END) 

In particolare per Oracle, saranno necessarie alcune ulteriori modifiche, senza cambiare la logica della risposta, solo perché Oracle non segue uno standard ANSI rigoroso in alcune aree. Testato su SQL-Fiddle-2


2
Questo è dolce. La matematica delle superiori è tornata a perseguitarmi. +1 !!!
— RolandoMySQLDBA il

1
Fantastica matematica, ma in realtà volevo sapere come creare una funzione aggregata in generale. productdoveva solo essere un esempio di diversi.
— Matt Fenwick,

Questo è piuttosto interessante, ma non funziona se nessuno dei valori è zero, poiché log (0) non è definito.
— jameshfisher,

@jameshfisher Correct. Si può scrivere facilmente la condizione extra, verificando la presenza di zeri (dove il prodotto sarebbe ovviamente zero). All'epoca non pensavo fosse necessario aggiungere quella complicazione.
— ypercubeᵀᴹ

Non mi è chiaro il modo migliore per aggiungere quella condizione. Non possiamo aggiungere la condizione nella funzione interna dei valori: poiché vogliamo quello PRODUCT(..., 0, ...) = 0, vogliamo quello EXP(SUM(..., f(0), ...)) = 0, per alcuni fche scegliamo, ma per soddisfarlo, ne abbiamo bisogno SUM(..., f(0), ...) = LOG(0)- ancora contrastato dallo stesso problema che registra (0 ) non è definito. Dobbiamo verificare la presenza di zero in qualche altro modo, ad es MIN(ABS(a)) = 0. Quindi avremmo SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. È questo il tipo di cosa a cui stavi pensando?
— jameshfisher,

3

Nell'interesse di imparare a pescare, ho compilato e installato con successo un "Hello, World!" UDF (funzione definita dall'utente) per MySQL trovato qui . Il file hello_world.so (dopo essere stato rispettato gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) dovrebbe essere archiviato in / usr / lib / mysql / plugins / con 755 autorizzazioni sui sistemi Linux Ubuntu. ["-I / usr / include / mysql" è il percorso dei file di intestazione mysql; Ho scoperto che il mio codice non si sarebbe compilato senza questo parametro, ma YMMV.]

Il programma non fa altro che stampare la stringa "Hello, World!" per ogni record nel set di dati risultante di una query, ma è tutto ciò che dovrebbe fare. Proverò a scrivere una piccola funzione aggregata nei prossimi giorni. C'è un esempio di una funzione aggregata che calcola il costo medio di un gruppo di record di prezzo e quantità; la funzione PICCOLA non dovrebbe essere così diversa da quella funzione alla fine.

Spero che sia di aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.