Как создать пользовательскую статистическую функцию?

8

Мне нужна агрегатная функция, которую MySQL не предоставляет.

Я хотел бы, чтобы он был в разновидности SQL MySQL (то есть, не в C).

Как мне это сделать? То, на чем я застрял, - это создание агрегатной функции - документы , похоже, не упоминают, как это делается.

Примеры желаемого использования productфункции:

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)
Мэтт Фенвик
источник

Ответы:

7

Согласно документации http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html , написать агрегатные функции можно только на C. Извините!

Колин т Харт
источник
Либо С, либо С ++. Во всяком случае, не SQL.
Майк Шеррилл 'Cat Recall'
1
Я предполагаю любой язык, который может генерировать двоичные библиотеки в поддерживаемом платформой двоичном формате с соглашениями о вызовах C.
Colin 't Hart
Я не знаю. Он был задокументирован как «C или C ++ (или другой язык, который может использовать соглашения о вызовах C)» в версии 5.0. Документы отброшены "или другой язык, который может использовать соглашения о вызовах C" в версии 5.1. Странная фраза, которую нужно отбросить.
Майк Шеррилл 'Cat Recall'
это доступно в последних версиях MySQL сейчас (через несколько лет)?
Динеш
9

Я не знаю, есть ли способ определить новую агрегатную функцию, не без возни с исходным кодом MySQL.

Но если все ваши числа положительны, вы можете получить арифметическую идентичность:

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

что вы можете использовать EXP(SUM(LOG(x)))для расчета PRODUCT(x). Тест в SQL-Fiddle :

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

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

Когда данные могут иметь 0, это становится немного сложнее:

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 ;

Проверено на SQL-Fiddle


Для других СУБД, которые не имеют автоматического преобразования MySQL логических значений в целые числа,

(NOT EXISTS (SELECT ...))

следует заменить на:

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

В частности, для Oracle потребуется внести еще несколько изменений, не меняя логику ответа, только потому, что Oracle в некоторых областях не следует строгому стандарту ANSI. Протестировано на SQL-Fiddle-2

ypercubeᵀᴹ
источник
2
Это мило. Средняя школа вернулась, чтобы преследовать меня. +1 !!!
RolandoMySQLDBA
1
Классная математика, но я на самом деле хотел знать, как создать агрегатную функцию в целом. productдолжен был быть лишь одним примером из нескольких.
Мэтт Фенвик
Это довольно круто, но не работает, если любое из значений равно нулю, поскольку log (0) не определено.
jameshfisher
@jameshfisher Правильно. Можно легко написать дополнительное условие, проверяя нули (где продукт, конечно, будет равен нулю). Я не думал, что в то время было необходимо добавить это осложнение.
ypercubeᵀᴹ
Мне не ясно, как лучше добавить это условие. Мы не можем добавить условие во внутреннюю функцию значений: так как мы хотим этого PRODUCT(..., 0, ...) = 0, мы хотим это EXP(SUM(..., f(0), ...)) = 0для тех f, кого мы выбираем, но чтобы удовлетворить это, нам нужно это SUM(..., f(0), ...) = LOG(0)- опять-таки помешано той же проблемой, что log (0 ) не определено. Нам нужно проверить наличие нуля другим способом, например MIN(ABS(a)) = 0. Так что мы бы получили SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. Вы думали об этом?
jameshfisher
3

В интересах научиться ловить рыбу я успешно скомпилировал и установил «Hello, World!» UDF (пользовательская функция) для MySQL находится здесь . Файл hello_world.so (после соблюдения gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) должен храниться в / usr / lib / mysql / plugins / с разрешениями 755 в системах Ubuntu linux. [-I / usr / include / mysql »- это путь к заголовочным файлам mysql; Я обнаружил, что мой код не будет компилироваться без этого параметра, но YMMV.]

Программа ничего не делает, кроме распечатки строки "Hello, World!" для каждой записи в результирующем наборе данных запроса, но это все, что он должен делать. Я постараюсь написать небольшую статистическую функцию в ближайшие несколько дней. Существует пример агрегатной функции, которая вычисляет среднюю стоимость группы записей цены и количества; функция SMALL не должна сильно отличаться от этой функции в конце.

Надеюсь это поможет.

Джеффри Роллан
источник