【问题标题】:MySQL group by all columns except oneMySQL 按除一列以外的所有列分组
【发布时间】:2015-06-30 05:53:15
【问题描述】:

我正在寻找一种(更清洁的?)方法来执行以下操作:

假设我有一个主表,有大约 15 列,看起来像这样,每个 id 一行:

主要的: id 开始结束 col4 ... col15 666 2014-01-01 2014-06-30 ... ... ... 1234 2015-03-05 2015-05-02 ... ... ... 9876 2014-09-01 2015-01-01 ... ... ... ...(ETC)

然后我有另一个表,事件,每个 id 可能有 0、1 或许多行:

事件: 身份证日期代码 666 2014-01-20 “code_a” 1234 2015-05-01 “code_b” 666 2014-01-25 “code_c” 666 2014-02-09 “code_z” ... (ETC)

最后我有一个表格,代码,每个代码有一行,给出代码的描述以及类型(0,1 或 2):

代码: 代码描述类型 “code_a” “某事” 0 “code_b”“其他”1 “code_c”“另一件事”0 “code_d” “还有一个” 2 (无代码 z)

我想要的结果是 main 的 15 列加上三个附加列,其中包含按类型在该 id 的开始日期和结束日期之间发生的事件代​​码的逗号分隔列表(第一列是类型 0,第二列是 1,第三种类型 2),所以:

id start end ... col15 type_0 type_1 type_2 666 2014-01-01 2014-06-30 ... ... "code_a,code_c" 1234 2015-03-05 2015-05-02 ... ... "code_b" ...(ETC)

我的解决方案是

select m.*
     , group_concat(c0.code) as type_0
     , group_concat(c1.code) as type_1
     , group_concat(c2.code) as type_2
from main m 
     left join events e on m.id = e.id and e.date between m.start and m.end
     left join codes c0 on c0.code = e.code and c0.type = 0
     left join codes c1 on c1.code = e.code and c1.type = 1
     left join codes c2 on c2.code = e.code and c2.type = 2
group by m.id
       , m.start
       , m.end
       , m.col4
       , m.col5
       , m.col6
       , m.col7
       , m.col8
       , m.col9
       , m.col10
       , m.col11
       , m.col12
       , m.col13
       , m.col14
       , m.col15  

但对我来说,这看起来很讨厌。有没有更优雅的方法来做到这一点(尤其是避免分组依据中列出的 15 列)?

【问题讨论】:

  • 射击。我确定这很清楚,但 c1 和 c2 的连接应该分别表示 c1.code、c1.type 和 c2.code、c2.type。
  • 您知道您可以编辑自己的问题吗?
  • 您的查询是否运行?我怀疑你的group_concat
  • 没有看到编辑按钮。修复了 group_concats 中的列和连接中的表名。 @Rahul 这是真正问题的简化版本,涉及更长的表名和列名......

标签: mysql sql


【解决方案1】:

在 MySQL 中,您可以只使用 GROUP BY m.id。除非您启用ONLY_FULL_GROUP_BY 选项,否则它允许您使用不在GROUP BY 子句中的非聚合列。如果您选择的列不是由分组列唯一标识的,这可能会产生不可预测的结果,但这里的情况并非如此——您按m 表的唯一 ID 列进行分组,并且所有非聚合列来自同一个表。

在严格的 SQL 中,您必须通过在子查询中执行 GROUP_CONCATs 来完成,然后将其与 main 表连接。

SELECT *
FROM (SELECT m.id,
            , group_concat(c0.code) as type_0
            , group_concat(c1.code) as type_1
            , group_concat(c2.code) as type_2
     FROM main m
     left join events e on m.id = e.id and e.date between m.start and m.end
     left join codes c0 on c0.code = e.code and c0.type = 0
     left join codes c1 on c1.code = e.code and c1.type = 1
     left join codes c2 on c2.code = e.code and c2.type = 2
     GROUP BY m.id
) t1
JOIN main m ON t1.id = m.id

【讨论】:

    【解决方案2】:

    通过“每个 id 一行”规范,您可以利用 MySQL 对 GROUP BY 的扩展,它允许您在 SELECT 列表中包含非聚合。您的查询所需的唯一更改就是

     GROUP BY m.id
    

    其他数据库会抛出错误。如果我们在会话的sql_mode 中包含ONLY_FULL_GROUP_BY,我们也可以让MySQL 抛出错误。


    另一种选择是避免对m 进行GROUP BY 操作,使用内联视图。您仍然需要执行GROUP BY,但您可以在内联视图中执行此操作,其中不返回来自main 的其他列,我们只返回唯一的id 值。我们需要它来连接外部查询。

    你似乎只需要一个加入codes 表;您可以在 GROUP_CONCAT 中使用条件测试来有条件地返回代码的值。

    例如:

    SELECT m.*
         , g.type_0
         , g.type_1
         , g.type_2
      FROM main m
      LEFT
      JOIN ( SELECT a.id
                  , GROUP_CONCAT(IF(c.type=0,c.code,NULL)) AS type_0
                  , GROUP_CONCAT(IF(c.type=1,c.code,NULL)) AS type_1
                  , GROUP_CONCAT(IF(c.type=2,c.code,NULL)) AS type_2
               FROM main a
               LEFT
               JOIN events e
                 ON e.id = a.id
                AND e.date BETWEEN a.start AND a.end
               LEFT
               JOIN codes c
                 ON c.code = e.code
                AND c.type IN (0,1,2)
              GROUP BY a.id
           ) g
        ON g.id = m.id
    

    我不确定其中任何一个是否符合“更优雅的方式”。 (这两者都依赖于id 列在main 中是唯一的。第二个查询还依赖于id 是非NULL。)

    您可能需要考虑在GROUP_CONCAT 中添加ORDER BY,以获得更确定的结果。如果没有理由在列表中返回 code 的“重复”值,也可以在 GROUP_CONCAT 中包含 DISTINCT 关键字,例如

        GROUP_CONCAT(DISTINCT IF(c.type=0,c.code,NULL) ORDER BY 1)\
    

    还要注意,从GROUP_CONCAT 返回的值的最大长度限制为group_concat_max_len

    【讨论】:

      【解决方案3】:

      另一个较短的版本如下所示,首先获取分组,然后加入它。

      select m.*
           , XX.type_0
           , XX.type_1
           , XX.type_2
      from main m 
           left join events e on m.id = e.id and e.date between m.start and m.end
           left join (
      select code, GROUP_CONCAT(case when type = 0 then code else null end SEPARATOR ', ') AS type_0,
      GROUP_CONCAT(case when type = 1 then code else null end SEPARATOR ', ') AS type_1,
      GROUP_CONCAT(case when type = 2 then code else null end SEPARATOR ', ') AS type_2
      from codes 
      group by <some_column> )XX ON XX.code = e.code;
      

      【讨论】:

        猜你喜欢
        • 2017-01-04
        • 2023-01-25
        • 1970-01-01
        • 1970-01-01
        • 2022-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多