Como crio uma função agregada definida pelo usuário?

8

Eu preciso de uma função agregada que o MySQL não fornece.

Eu gostaria que estivesse no sabor do SQL do MySQL (ou seja, não em C).

Como eu faço isso? O que eu estou preso é criar uma função agregada - os documentos não parecem mencionar como isso é feito.

Exemplos de uso desejado de uma productfunção:

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
fonte

Respostas:

7

De acordo com a documentação http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html , só é possível escrever funções agregadas em C. Desculpe!

Colin 't Hart
fonte
C ou C ++. Não é SQL, de qualquer maneira.
Mike Sherrill 'Cat Recall'
11
Presumo que qualquer linguagem que possa gerar bibliotecas binárias no formato binário suportado pela plataforma com convenções de chamada em C.
Colin 't Hart
Eu não sei. Foi documentado como "C ou C ++ (ou outro idioma que pode usar convenções de chamada C)" na versão 5.0. Os documentos descartados "ou outro idioma que pode usar convenções de chamada C" na versão 5.1. Essa é uma frase estranha para largar.
Mike Sherrill 'Recall Cat'
está disponível nas versões recentes do mysql agora (depois de alguns anos)?
Dinesh
9

Não sei se há como definir uma nova função agregada, não sem mexer no código fonte do MySQL.

Mas se todos os seus números forem positivos, você pode derivar da identidade aritmética:

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

que você pode usar EXP(SUM(LOG(x)))para calcular PRODUCT(x). Teste no 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 os dados podem ter 0s, fica um pouco mais 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 ;

Testado no SQL-Fiddle


Para outros DBMS, que não têm a conversão automática do MySQL de valores booleanos em números inteiros, o

(NOT EXISTS (SELECT ...))

deve ser substituído por:

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

Especificamente para a Oracle, serão necessárias mais algumas alterações, sem alterar a lógica da resposta, apenas porque a Oracle não segue o padrão ANSI estrito em algumas áreas. Testado no SQL-Fiddle-2

ypercubeᵀᴹ
fonte
2
Isso e doce. A matemática do ensino médio voltou para me assombrar. +1 !!!
RolandoMySQLDBA
11
Matemática legal, mas eu realmente queria saber como criar uma função agregada em geral. productdeveria ser apenas um exemplo de vários.
Matt Fenwick
Isso é bem legal, mas não funciona se algum dos valores for zero, pois o log (0) é indefinido.
Jameshfisher
@jameshfisher Correto. Pode-se escrever facilmente a condição extra, verificando zeros (onde o produto seria zero, é claro). Na época, não achei necessário adicionar essa complicação.
precisa saber é o seguinte
Não está claro para mim a melhor forma de adicionar essa condição. Não podemos adicionar a condição na função interna dos valores: como queremos isso PRODUCT(..., 0, ...) = 0, queremos que EXP(SUM(..., f(0), ...)) = 0, para alguns fque escolhemos, mas para satisfazer isso, precisamos disso SUM(..., f(0), ...) = LOG(0)- novamente frustrados pelo mesmo problema que registramos (0 ) está indefinido. Precisamos verificar a presença de zero de alguma outra maneira, por exemplo MIN(ABS(a)) = 0. Então nós teríamos SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. É esse o tipo de coisa que você estava pensando?
Jameshfisher
3

No interesse de aprender a pescar, compilei e instalei com sucesso um "Olá, Mundo!" UDF (função definida pelo usuário) para MySQL encontrada aqui . O arquivo hello_world.so (depois de cumprido gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) deve ser armazenado em / usr / lib / mysql / plugins / com permissões 755 nos sistemas Linux Ubuntu. [O "-I / usr / include / mysql" é o caminho para os arquivos de cabeçalho do mysql; Eu descobri que meu código não seria compilado sem esse parâmetro, mas YMMV.]

O programa não faz nada além de imprimir a string "Olá, mundo!" para cada registro no conjunto de dados resultante de uma consulta, mas é tudo o que deve fazer. Vou tentar escrever uma função agregada PEQUENA nos próximos dias. Há um exemplo de uma função agregada que calcula o custo médio de um grupo de registros de preço e quantidade; a função SMALL não deve ser tão diferente dessa função no final.

Espero que isto ajude.

Jeffrey Rolland
fonte