Wie erstelle ich eine benutzerdefinierte Aggregatfunktion?

8

Ich benötige eine Aggregatfunktion, die MySQL nicht bietet.

Ich möchte, dass es in MySQLs SQL-Version ist (dh nicht in C).

Wie mache ich das? Ich bleibe beim Erstellen einer Aggregatfunktion - die Dokumente scheinen nicht zu erwähnen, wie dies gemacht wird.

Beispiele für die gewünschte Verwendung einer productFunktion:

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
quelle

Antworten:

7

Laut der Dokumentation http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html ist es nur möglich, Aggregatfunktionen in C zu schreiben. Sorry!

Colin 't Hart
quelle
Entweder C oder C ++. Jedenfalls nicht SQL.
Mike Sherrill 'Cat Recall'
1
Ich nehme jede Sprache an, die Binärbibliotheken im plattformunterstützten Binärformat mit C-Aufrufkonventionen generieren kann.
Colin 't Hart
Ich weiß es nicht. Es wurde in Version 5.0 als "C oder C ++ (oder eine andere Sprache, die C-Aufrufkonventionen verwenden kann)" dokumentiert . Die Dokumente wurden in Version 5.1 "oder eine andere Sprache, die C-Aufrufkonventionen verwenden kann" gelöscht. Das ist ein seltsamer Satz.
Mike Sherrill 'Cat Recall'
ist es jetzt (nach einigen Jahren) in den neuesten MySQL-Versionen verfügbar?
Dinesh
9

Ich weiß nicht, ob es eine Möglichkeit gibt, eine neue Aggregatfunktion zu definieren, nicht ohne mit dem MySQL-Quellcode herumzuspielen.

Wenn Ihre Zahlen jedoch alle positiv sind, können Sie sich durchaus aus der arithmetischen Identität ableiten:

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

die Sie verwenden können, um EXP(SUM(LOG(x)))zu berechnen 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 ;

Wenn die Daten Nullen haben können, wird es etwas komplizierter:

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 ;

Getestet bei SQL-Fiddle


Für andere DBMS, die keine automatische Konvertierung von Booleschen Werten in Ganzzahlen durch MySQL haben, ist die

(NOT EXISTS (SELECT ...))

sollte ersetzt werden durch:

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

Speziell für Oracle sind einige weitere Änderungen erforderlich, ohne die Logik der Antwort zu ändern, nur weil Oracle in einigen Bereichen nicht dem strengen ANSI-Standard folgt. Getestet bei SQL-Fiddle-2

ypercubeᵀᴹ
quelle
2
Das ist süß. Die Mathematik der High School ist zurückgekommen, um mich zu verfolgen. +1 !!!
RolandoMySQLDBA
1
Coole Mathematik, aber ich wollte eigentlich wissen, wie man eine Aggregatfunktion im Allgemeinen erstellt. productsollte nur ein Beispiel von mehreren sein.
Matt Fenwick
Das ist ziemlich cool, funktioniert aber nicht, wenn einer der Werte Null ist, da log (0) undefiniert ist.
Jameshfisher
@ Jameshfisher Richtig. Man kann die zusätzliche Bedingung leicht schreiben und nach Nullen suchen (wobei das Produkt natürlich Null wäre). Ich dachte damals nicht, dass es notwendig wäre, diese Komplikation hinzuzufügen.
Ypercubeᵀᴹ
Mir ist nicht klar, wie ich diese Bedingung am besten hinzufügen kann. Wir können die Bedingung nicht in die innere Funktion der Werte einfügen: Da wir das wollen PRODUCT(..., 0, ...) = 0, wollen wir das EXP(SUM(..., f(0), ...)) = 0für einige f, die wir wählen, aber um dies zu erfüllen, brauchen wir das SUM(..., f(0), ...) = LOG(0)- wieder vereitelt durch dasselbe Problem wie log (0) ) ist nicht definiert. Wir müssen auf andere Weise prüfen, ob Null vorhanden ist, z MIN(ABS(a)) = 0. Also hätten wir SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. Ist das die Art von Dingen, an die du gedacht hast?
Jameshfisher
3

Um das Fischen zu lernen, habe ich erfolgreich ein "Hallo Welt!" Kompiliert und installiert. UDF (benutzerdefinierte Funktion) für MySQL finden Sie hier . Die Datei hello_world.so (nach Einhaltung gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) sollte in / usr / lib / mysql / plugins / mit 755 Berechtigungen auf Ubuntu-Linux-Systemen gespeichert werden. ["-I / usr / include / mysql" ist der Pfad zu den MySQL-Header-Dateien. Ich habe festgestellt, dass mein Code ohne diesen Parameter nicht kompiliert werden kann, aber YMMV.]

Das Programm druckt nur die Zeichenfolge "Hallo Welt!" für jeden Datensatz im resultierenden Datensatz einer Abfrage, aber das ist alles, was es tun soll. Ich werde versuchen, in den nächsten Tagen eine kleine Aggregatfunktion zu schreiben. Es gibt ein Beispiel für eine Aggregatfunktion, die die durchschnittlichen Kosten einer Gruppe von Preis- und Mengenaufzeichnungen berechnet. Die SMALL-Funktion sollte sich am Ende nicht so sehr von dieser Funktion unterscheiden.

Hoffe das hilft.

Jeffrey Rolland
quelle