在 mysql 中创建与 GROUP BY 一起使用的用户定义函数
Create a user defined function that works with GROUP BY in mysql
我正在尝试在 MySQL 中创建聚合函数 MEDIAN(),例如 MIN()、MAX()、AVG(),它的输入是具有所需的列。
我无法理解 MySQL 自定义函数的局限性,如果有人能帮助我了解这是如何完成的,那将非常有帮助。
示例:
MySQL table 有 2 列 (ID, num)
+----+-----+
| id | num |
+----+-----+
| 1 | 5 |
| 1 | 6 |
| 1 | 7 |
| 2 | 1 |
| 2 | 3 |
| 2 | 5 |
+----+-----+
SELECT id, MEDIAN(num) as median
FROM table
GROUP BY id;
或
SELECT id, MEDIAN(GROUP_CONCAT(num SEPARATOR ',') as median
FROM table
GROUP BY id;
预期输出为
+----+--------+
| id | median |
+----+--------+
| 1 | 6 |
| 2 | 3 |
+----+--------+
User defined aggregate stored functions 已添加到 MariaDB-10.3.3
MySQL 可以执行聚合函数,但不能在 SQL 中执行。他们需要 UDF (shared library implemenation)
编辑:我知道这个答案并没有直接解决问题,因为问题是“如何在 mySQL 中创建聚合中值函数”,我的回答具体说明了如何在没有UDF.
但是,接受的答案说在 mySQL 中不可能无论如何汇总中位数。
没有 UDF 也是可以的,我知道有两种方法可以做到。第一个使用两个 select 和一个连接,第一个 select 获取值和排名,第二个 select 获取计数,然后连接它们。第二个使用 json 函数将所有内容集中在一个 select 中。它们都有点冗长,但它们可以工作并且速度相当快。
解决方案 #1(两个 select 和一个连接,一个用于获取计数,一个用于获取排名)
SELECT x.group_field,
avg(
if(
x.rank - y.vol/2 BETWEEN 0 AND 1,
value_field,
null
)
) as median
FROM (
SELECT group_field, value_field,
@r:= IF(@current=group_field, @r+1, 1) as rank,
@current:=group_field
FROM (
SELECT group_field, value_field
FROM table_name
ORDER BY group_field, value_field
) z, (SELECT @r:=0, @current:='') v
) x, (
SELECT group_field, count(*) as vol
FROM table_name
GROUP BY group_field
) y WHERE x.group_field = y.group_field
GROUP BY x.group_field;
解决方案 #2(使用 json 对象存储计数并避免连接)
SELECT group_field,
avg(
if(
rank - json_extract(@vols, path)/2 BETWEEN 0 AND 1,
value_field,
null
)
) as median
FROM (
SELECT group_field, value_field, path,
@rnk := if(@curr = group_field, @rnk+1, 1) as rank,
@vols := json_set(
@vols,
path,
coalesce(json_extract(@vols, path), 0) + 1
) as vols,
@curr := group_field
FROM (
SELECT p.group_field, p.value_field, concat('$.', p.group_field) as path
FROM table_name
JOIN (SELECT @curr:='', @rnk:=1, @vols:=json_object()) v
ORDER BY group_field, value_field DESC
) z
) y GROUP BY group_field;
我正在尝试在 MySQL 中创建聚合函数 MEDIAN(),例如 MIN()、MAX()、AVG(),它的输入是具有所需的列。
我无法理解 MySQL 自定义函数的局限性,如果有人能帮助我了解这是如何完成的,那将非常有帮助。
示例:
MySQL table 有 2 列 (ID, num)
+----+-----+
| id | num |
+----+-----+
| 1 | 5 |
| 1 | 6 |
| 1 | 7 |
| 2 | 1 |
| 2 | 3 |
| 2 | 5 |
+----+-----+
SELECT id, MEDIAN(num) as median
FROM table
GROUP BY id;
或
SELECT id, MEDIAN(GROUP_CONCAT(num SEPARATOR ',') as median
FROM table
GROUP BY id;
预期输出为
+----+--------+
| id | median |
+----+--------+
| 1 | 6 |
| 2 | 3 |
+----+--------+
User defined aggregate stored functions 已添加到 MariaDB-10.3.3
MySQL 可以执行聚合函数,但不能在 SQL 中执行。他们需要 UDF (shared library implemenation)
编辑:我知道这个答案并没有直接解决问题,因为问题是“如何在 mySQL 中创建聚合中值函数”,我的回答具体说明了如何在没有UDF.
但是,接受的答案说在 mySQL 中不可能无论如何汇总中位数。
没有 UDF 也是可以的,我知道有两种方法可以做到。第一个使用两个 select 和一个连接,第一个 select 获取值和排名,第二个 select 获取计数,然后连接它们。第二个使用 json 函数将所有内容集中在一个 select 中。它们都有点冗长,但它们可以工作并且速度相当快。
解决方案 #1(两个 select 和一个连接,一个用于获取计数,一个用于获取排名)
SELECT x.group_field,
avg(
if(
x.rank - y.vol/2 BETWEEN 0 AND 1,
value_field,
null
)
) as median
FROM (
SELECT group_field, value_field,
@r:= IF(@current=group_field, @r+1, 1) as rank,
@current:=group_field
FROM (
SELECT group_field, value_field
FROM table_name
ORDER BY group_field, value_field
) z, (SELECT @r:=0, @current:='') v
) x, (
SELECT group_field, count(*) as vol
FROM table_name
GROUP BY group_field
) y WHERE x.group_field = y.group_field
GROUP BY x.group_field;
解决方案 #2(使用 json 对象存储计数并避免连接)
SELECT group_field,
avg(
if(
rank - json_extract(@vols, path)/2 BETWEEN 0 AND 1,
value_field,
null
)
) as median
FROM (
SELECT group_field, value_field, path,
@rnk := if(@curr = group_field, @rnk+1, 1) as rank,
@vols := json_set(
@vols,
path,
coalesce(json_extract(@vols, path), 0) + 1
) as vols,
@curr := group_field
FROM (
SELECT p.group_field, p.value_field, concat('$.', p.group_field) as path
FROM table_name
JOIN (SELECT @curr:='', @rnk:=1, @vols:=json_object()) v
ORDER BY group_field, value_field DESC
) z
) y GROUP BY group_field;