¿Cómo creo una función agregada definida por el usuario?

8

Necesito una función agregada que MySQL no proporciona.

Me gustaría que estuviera en el sabor de SQL de MySQL (es decir, no en C).

¿Cómo hago esto? Lo que estoy atascado es crear una función agregada; los documentos no parecen mencionar cómo se hace.

Ejemplos del uso deseado de una productfunción:

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)
Matt Fenwick
fuente

Respuestas:

7

De acuerdo con la documentación http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html solo es posible escribir funciones agregadas en C. ¡Lo siento!

Colin 't Hart
fuente
C o C ++. No SQL, de todos modos.
Mike Sherrill 'Cat Recall'
1
Presumo cualquier lenguaje que pueda generar bibliotecas binarias en el formato binario compatible con la plataforma con convenciones de llamadas C.
Colin 't Hart
No lo sé. Fue documentado como "C o C ++ (u otro lenguaje que puede usar convenciones de llamadas C)" en la versión 5.0. Los documentos se descartaron "u otro lenguaje que pueda usar convenciones de llamadas C" en la versión 5.1. Esa es una frase extraña para soltar.
Mike Sherrill 'Cat Recall'
¿Está disponible en versiones recientes de MySQL ahora (después de unos años)?
Dinesh
9

No sé si hay forma de definir una nueva función agregada, no sin jugar con el código fuente de MySQL.

Pero si todos sus números son positivos, bien puede derivar de la identidad aritmética:

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

que puedes usar EXP(SUM(LOG(x)))para calcular PRODUCT(x). Prueba en SQL-Fiddle :

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

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

Cuando los datos pueden tener ceros, se vuelve un poco más complicado:

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 ;

Probado en SQL-Fiddle


Para otros DBMS, que no tienen la conversión automática de valores booleanos a enteros de MySQL, el

(NOT EXISTS (SELECT ...))

debe reemplazarse con:

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

Específicamente para Oracle, se necesitarán algunos cambios más, sin cambiar la lógica de la respuesta, solo porque Oracle no sigue el estricto estándar ANSI en algunas áreas. Probado en SQL-Fiddle-2

ypercubeᵀᴹ
fuente
2
Eso es dulce. Las matemáticas de la secundaria han vuelto para perseguirme. +1 !!!
RolandoMySQLDBA
1
Matemáticas geniales, pero en realidad quería saber cómo crear una función agregada en general. productse suponía que era un ejemplo de varios.
Matt Fenwick el
Esto es bastante bueno, pero no funciona si alguno de los valores es cero, ya que log (0) no está definido.
jameshfisher
@jameshfisher Correcto. Se puede escribir fácilmente la condición adicional, verificando ceros (donde el producto sería cero, por supuesto). En ese momento no pensé que fuera necesario agregar esa complicación.
ypercubeᵀᴹ
No me queda claro cuál es la mejor manera de agregar esa condición. No podemos agregar la condición en la función interna de los valores: ya que queremos eso PRODUCT(..., 0, ...) = 0, queremos eso EXP(SUM(..., f(0), ...)) = 0, para algunos fque elegimos, pero para satisfacer esto, necesitamos eso SUM(..., f(0), ...) = LOG(0), nuevamente frustrado por el mismo problema que log (0 ) es indefinido. Necesitamos verificar la presencia de cero de alguna otra manera, por ejemplo MIN(ABS(a)) = 0. Entonces lo habríamos hecho SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. ¿Es este el tipo de cosas en las que estabas pensando?
jameshfisher
3

En aras de aprender a pescar, he compilado e instalado con éxito un "¡Hola, mundo!" UDF (función definida por el usuario) para MySQL que se encuentra aquí . El archivo hello_world.so (después de cumplirlo gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) debe almacenarse en / usr / lib / mysql / plugins / con 755 permisos en los sistemas Linux de Ubuntu. ["-I / usr / include / mysql" es la ruta a los archivos de encabezado mysql; Descubrí que mi código no se compilaría sin este parámetro, pero YMMV.]

El programa no hace más que imprimir la cadena "¡Hola, mundo!" para cada registro en el conjunto de datos resultante de una consulta, pero eso es todo lo que se supone que debe hacer. Intentaré escribir una función agregada PEQUEÑA en los próximos días. Hay un ejemplo de una función agregada que calcula el costo promedio de un grupo de registros de precio y cantidad; la función PEQUEÑA no debería ser tan diferente de esa función al final.

Espero que esto ayude.

Jeffrey Rolland
fuente