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.