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

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

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

规则是:

    对于每个组,将streetcity 的非空值优先于空值。 在每个组中,streetcity 列的值从 ab 源优先于 c 源(权重(a)= 权重(b)> 权重(@987654344 @)) 如果这些列不为空。 对于源 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 + 命令式处理任务。或者只是使用一些工具从数据库中提取信息,然后对其进行处理。

附:这是一个简化的例子。

【问题讨论】:

第一个和第二个条件可以在GROUP BY 查询中验证,但第三个条件不能。您不能使用GROUP BY 选择行。 GROUP BY 使用来自每个组的数据计算新行。您不能使用GROUP BY 选择行。 GROUP BY 使用来自每个组的数据计算新行。在similar (but much simpler) question 上查看this answer。 每次我需要从表中获取值时,我都不会编写一个怪物查询(有或没有GROUP BY)来运行。我会将其视为草稿表,并创建另一个表来为每一对(zipphone)存储一行,并使用您需要的任何标准来管理其他字段。需要数据的脚本将从“干净”表中读取;另一个脚本将使用“草稿”表来计算“干净”记录。这可以定期运行(并处理所有行)或仅在将新行添加到“草稿”时运行(并仅重新计算受影响的“干净”记录)。 在另一个 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 字段可以使用类似的查询。

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

【讨论】:

以上是关于MySQL group by 具有多个选择规则的多列的排序和优先级的主要内容,如果未能解决你的问题,请参考以下文章

加速使用 Group By 和 Order By 的多表 Mysql 查询

sum()在具有多个联接的MySQL查询中不能正常工作(group by不能按预期工作)

从 GROUP BY 组中选择具有特定内容的行

在 MySQL 中使用 Case 加入 Group By & Order

【MySQL】分组查询(GROUP BY)

mysql 可以group by 两个字段吗