MySQL group by 具有多个选择规则的多列的排序和优先级
Posted
技术标签:
【中文标题】MySQL group by 具有多个选择规则的多列的排序和优先级【英文标题】:MySQL group by with ordering and priority of multiple columns with multiple selection rules 【发布时间】:2018-02-15 03:16:35 【问题描述】:我的问题可能类似于这些问题:
mysql group by with ordering/priority of another column Grouping by Column with Dependence on another Column MySQL GROUP BY with preference表格示例:
source zip phone street city created_at
==================================================================
a 11111 11111 Flatlands null 2015-01-01
b 11111 11111 Flatlands Avenue New York 2015-01-01
c 11111 11111 Ave Flatlands New York 2015-01-01
a 22222 22222 Favory New York 2015-01-01
b 22222 22222 Favory Avenue New York 2017-12-12
c 22222 22222 Ave Favory New York 2015-01-01
b 33333 33333 Sixteenth Washington 2015-01-01
c 33333 33333 st. 16th null 2015-01-01
c 44444 44444 st. West Land null 2015-01-01
假设我有一张表格,其中包含有关不同城市地点的信息。这些信息来自 3 个不同的来源:a
、b
和 c
。
zip
和 phone
字段唯一标识位置,因此数据库中的行可以按这些字段分组。
我需要合并来自不同来源的不同位置的信息,根据street
和city
列的规则集选择最佳值。
规则是:
-
对于每个组,将
street
和city
的非空值优先于空值。
在每个组中,street
和 city
列的值从 a
和 b
源优先于 c
源(权重(a
)= 权重(b
)> 权重(@987654344 @)) 如果这些列不为空。
对于源 a
和 b
,优先考虑具有最新 created_at
时间戳的行中的列值。
这是我想要收到的结果:
zip phone street city
====================================
11111 11111 Flatlands New York
22222 22222 Favory Avenue New York
33333 33333 Sixteenth Washington
44444 44444 st. West Land null
Here is a DB Fiddle to play with.
我不确定这是否可以通过 SQL 实现,也许我最好的选择是切换到 NoSQL DB + 命令式处理任务。或者只是使用一些工具从数据库中提取信息,然后对其进行处理。
附:这是一个简化的例子。
【问题讨论】:
第一个和第二个条件可以在GROUP BY
查询中验证,但第三个条件不能。您不能使用GROUP BY
选择行。 GROUP BY
使用来自每个组的数据计算新行。您不能使用GROUP BY
选择行。 GROUP BY
使用来自每个组的数据计算新行。在similar (but much simpler) question 上查看this answer。
每次我需要从表中获取值时,我都不会编写一个怪物查询(有或没有GROUP BY
)来运行。我会将其视为草稿表,并创建另一个表来为每一对(zip
、phone
)存储一行,并使用您需要的任何标准来管理其他字段。需要数据的脚本将从“干净”表中读取;另一个脚本将使用“草稿”表来计算“干净”记录。这可以定期运行(并处理所有行)或仅在将新行添加到“草稿”时运行(并仅重新计算受影响的“干净”记录)。
在另一个 DBMS 中,您只需使用带有适当分区和排序顺序的 ROW_NUMBER
来对记录进行排序。由于 MySQL 缺少 ROW_NUMBER
,请查看如何在 MySQL 中使用变量模拟 ROW_NUMBER
。
【参考方案1】:
您可以使用以下查询来实现street
的优先级规则:
SELECT zip, phone, street
FROM test
ORDER BY zip, phone,
-- prioritize non empty values over null values
CASE
WHEN (street IS NOT NULL) OR (street = '') THEN 0
ELSE 1
END,
-- prioritize a, b over c
CASE
WHEN source IN ('a', 'b') THEN 0
ELSE 1
END,
-- prioritize rows which have the latest created_at
created_at DESC
类似的查询可以用于city
字段。
然后你可以模拟ROW_NUMBER
,不幸的是在MySQL中不可用,使用变量:
SELECT zip, phone, street,
@seq := IF(@id = CONCAT(zip,phone), @seq + 1,
IF(@id := CONCAT(zip,phone), 1, 1)) AS seq
FROM test
CROSS JOIN (SELECT @seq := 0, @id = '') AS v
ORDER BY zip, phone,
-- prioritize non empty values over null values
CASE
WHEN (street IS NOT NULL) OR (street = '') THEN 0
ELSE 1
END,
-- prioritize a, b over c
CASE
WHEN source IN ('a', 'b') THEN 0
ELSE 1
END,
-- prioritize rows which have the latest created_at
created_at DESC
同样,city
字段可以使用类似的查询。
通过在zip
、street
和seq = 1
上加入上述派生表可以获得所需的结果。
【讨论】:
以上是关于MySQL group by 具有多个选择规则的多列的排序和优先级的主要内容,如果未能解决你的问题,请参考以下文章
加速使用 Group By 和 Order By 的多表 Mysql 查询
sum()在具有多个联接的MySQL查询中不能正常工作(group by不能按预期工作)