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

时间:2017-09-06 14:55:36

标签: 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的非空值优先于空值。
  2. 在每个组中优先考虑来自streetcity来源的ab列的值c来源(权重(a)) =权重(b)>权重(c))如果这些列不为空。
  3. 对于来源ab,请对具有最新created_at时间戳的行中的列值进行优先排序。
  4. 以下是我想收到的结果:

    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 +命令式处理任务。或者只是使用一些工具从数据库中提取信息然后进行处理。

    P.S。这是一个简化的例子。

1 个答案:

答案 0 :(得分:0)

您可以使用以下查询来实现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上加入上述派生表来获得所需的结果。