MySQL group by 具有多个选择规则的多列的排序和优先级

标签 mysql sql group-by sql-order-by

我的问题可能与这些问题类似:

表格示例:

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 个不同的来源收集:abc

zipphone 字段唯一标识位置,因此数据库中的行可以按这些字段分组。

我需要合并来自不同来源的不同位置的信息,根据 streetcity 列的规则集选择最佳值。

规则是:

  1. 对于每个组,streetcity 的非空值优先于 null 值。
  2. 在每个组中,ab 来源的 streetcity 列的值优先于 c source (weight(a) = weight(b) > weight(c)) 如果这些列不为空。
  3. 对于源 ab,优先考虑具有最新 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 + 命令式处理任务。或者只是使用一些工具从数据库中提取信息,然后对其进行处理。

附言这是一个简化的示例。

最佳答案

您可以使用以下查询来实现 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 字段。

通过在 zipstreetseq = 1 上连接上述派生表,可以获得所需的结果。

关于MySQL group by 具有多个选择规则的多列的排序和优先级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46078468/

相关文章:

php - 数据库中的`position`列,如何重新排序?

sql - 减去两个子查询

SQL Server : "Conversion failed when converting datetime from character string."

postgresql - 将计算值添加为新列,同时删除重复项

mysql - 同一数据库中是否可以有不同的字符集?

php - 高级 SQL 查询。每个类别的前 12 名 (MYSQL)

php - 使用 PHP 和 MySQL 同时更新具有不同值的多个用户数据

MySQL 从连接表中计算两个组

pandas - 在 Pandas 中聚合多列时如何重置索引

mysql - 在 mysql 表名中使用 (-) 破折号