Bagaimana cara membuat fungsi agregat yang ditentukan pengguna?

8

Saya membutuhkan fungsi agregat yang tidak disediakan oleh MySQL.

Saya ingin itu dalam rasa MySQL SQL (yaitu, bukan dalam C).

Bagaimana saya melakukan ini? Apa yang saya terjebak adalah menciptakan fungsi agregat - dokumen sepertinya tidak menyebutkan bagaimana ini dilakukan.

Contoh penggunaan productfungsi yang diinginkan :

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
sumber

Jawaban:

7

Menurut dokumentasi http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html , hanya dimungkinkan untuk menulis fungsi agregat dalam C. Maaf!

Colin 't Hart
sumber
Baik C atau C ++. Bukan SQL.
Mike Sherrill 'Cat Recall'
1
Saya menduga bahasa apa pun yang dapat menghasilkan perpustakaan biner dalam format biner yang didukung platform dengan konvensi pemanggilan C.
Colin 't Hart
Saya tidak tahu Itu didokumentasikan sebagai "C atau C ++ (atau bahasa lain yang dapat menggunakan konvensi pemanggilan C)" di versi 5.0. Dokumen dihapus "atau bahasa lain yang dapat menggunakan konvensi pemanggilan C" di versi 5.1. Itu ungkapan aneh untuk dijatuhkan.
Mike Sherrill 'Cat Recall'
apakah tersedia di versi mysql terbaru sekarang (setelah beberapa tahun)?
Dinesh
9

Saya tidak tahu apakah ada cara untuk mendefinisikan fungsi agregat baru, bukan tanpa mengacaukan kode sumber MySQL.

Tetapi jika angka-angka Anda semuanya positif, Anda mungkin berasal dari identitas aritmatika:

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

yang dapat Anda gunakan EXP(SUM(LOG(x)))untuk menghitung PRODUCT(x). Uji dalam SQL-Fiddle :

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

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

Ketika data dapat memiliki 0s, itu menjadi sedikit lebih rumit:

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 ;

Diuji di SQL-Fiddle


Untuk DBMS lain, yang tidak memiliki konversi otomatis nilai boolean MySQL ke integer, the

(NOT EXISTS (SELECT ...))

harus diganti dengan:

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

Khusus untuk Oracle, beberapa perubahan akan diperlukan, tanpa mengubah logika jawabannya, hanya karena Oracle tidak mengikuti standar ANSI yang ketat di beberapa area. Diuji di SQL-Fiddle-2

ypercubeᵀᴹ
sumber
2
Itu manis. Matematika sekolah menengah telah kembali menghantui saya. +1 !!!
RolandoMySQLDBA
1
Matematika keren, tapi saya sebenarnya ingin tahu cara membuat fungsi agregat secara umum. productseharusnya hanya menjadi salah satu contoh dari beberapa.
Matt Fenwick
Ini cukup keren, tetapi tidak berfungsi jika salah satu nilainya nol, karena log (0) tidak ditentukan.
jameshfisher
@ jameshfisher Benar. Satu dapat dengan mudah menulis kondisi ekstra, memeriksa nol (di mana produk tentu saja nol). Saya tidak berpikir pada saat itu perlu menambahkan komplikasi itu.
ypercubeᵀᴹ
Tidak jelas bagi saya cara terbaik untuk menambahkan kondisi itu. Kita tidak dapat menambahkan kondisi dalam fungsi dalam nilai-nilai: karena kita menginginkannya PRODUCT(..., 0, ...) = 0, kita menginginkan itu EXP(SUM(..., f(0), ...)) = 0, untuk beberapa fyang kita pilih, tetapi untuk memenuhi ini, kita memerlukan itu SUM(..., f(0), ...) = LOG(0)- lagi-lagi digagalkan oleh masalah yang sama yang dicatat (0) ) tidak terdefinisi. Kita perlu memeriksa keberadaan nol dengan cara lain, misalnya MIN(ABS(a)) = 0. Jadi kita harus melakukannya SELECT CASE WHEN MIN(ABS(a)) = 0 THEN 0 ELSE EXP(SUM(LOG(a))) END AS product. Apakah ini jenis hal yang Anda pikirkan?
jameshfisher
3

Untuk mempelajari cara memancing, saya telah berhasil menyusun dan menginstal "Halo, Dunia!" UDF (fungsi yang ditentukan pengguna) untuk MySQL ditemukan di sini . File hello_world.so (setelah dipatuhi gcc -shared -o hello_world.so -I /usr/include/mysql hello_world.c) harus disimpan di / usr / lib / mysql / plugins / dengan 755 izin pada sistem linux Ubuntu. ["-I / usr / include / mysql" adalah path ke file header mysql; Saya menemukan kode saya tidak dapat dikompilasi tanpa parameter ini, tetapi YMMV.]

Program tidak melakukan apa pun selain mencetak string "Halo, Dunia!" untuk setiap record dalam dataset yang dihasilkan dari sebuah query, tetapi hanya itu yang harus dilakukan. Saya akan mencoba menulis fungsi agregat KECIL dalam beberapa hari mendatang. Ada contoh fungsi agregat yang menghitung biaya rata-rata sekelompok catatan harga dan kuantitas; fungsi KECIL seharusnya tidak jauh berbeda dari fungsi itu pada akhirnya.

Semoga ini membantu.

Jeffrey Rolland
sumber