除了一个

时间:2015-06-29 20:41:53

标签: mysql sql

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

让我们说我有一个表,main,有~15列看起来像这样,每个id有一行:

main:
id      start           end             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行或多行:

events:
id      date            code
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):

codes:
code            desc            type
"code_a"        "something"     0 
"code_b"        "somethn else"  1
"code_c"        "another thing" 0
"code_d"        "one more"      2
(no code z)

和我想要的结果是主要的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列)?

3 个答案:

答案 0 :(得分:1)

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

在严格的SQL中,您必须通过在子查询中执行GROUP_CONCAT来执行此操作,然后将其与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

答案 1 :(得分:0)

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

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;

答案 2 :(得分:0)

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

 GROUP BY m.id

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

另一种方法是使用内联视图避免对GROUP BY进行m操作。您仍然需要执行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

我不确定其中任何一个都符合&#34;更优雅的方式&#34;或不。 (这两个都取决于idmain列为UNIQUE。第二个查询还依赖于id非NULL。)

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

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

另请注意,GROUP_CONCAT返回的值的最大长度仅限于group_concat_max_len