【问题标题】:Most recent value per column with multiple column group by and where clause具有多列 group by 和 where 子句的每列的最新值
【发布时间】:2013-05-15 19:13:53
【问题描述】:

给定这个 200 万+ 条目表,ID 自动递增,以及 index1(MainId,SubID,Column1) index2(MainId,SubID,Column2):

ID  MainID  SubID  Column1    Column2 
--------------------------------------
1     1       A   1A_data_1           
2     1       A              1A_data_2
3     2       B   2B_data_1           
4     2       B              2B_data_2
5     1       A   ignore_me             
6     1       A              1A_data_3

我可以使用以下索引获取包含所需列值的行 ID:

Select max(ID) 
From table where column1 is not null and column1 <>'ignore_me'
Group By MainID,SubID

Select max(id) 
From table where column2 is not null and column2 <>'ignore_me'
Group By MainID,SubID

但我不能做的是找到一种有效的方法将这些与 MainID、SubID 组结合起来以获得这些结果:

MainID  SubID  Column1    Column2
--------------------------------
  1       A   1A_data_1  1A_data_3
  2       B   2B_data_1  2B_data_2

我尝试了很多不同的方法,但没有什么是不会永远持续下去的。我需要另一个索引吗?我觉得我忽略了一些简单的事情,因为查询分组非常快。谁能指出我正确的方向?

【问题讨论】:

  • 检查其他方法的执行计划,它们会指示是否需要索引(并不总是正确的)。如果您还没有,不妨试试 Red Gate 的 Plan Explorer,非常不错(而且免费)的工具。
  • 首先,感谢您的回复,我将检查 Red Gate 的计划探索。作为记录,我检查了执行计划,但找不到一个好的计划。我什至跑了我能想到的每一个坏的,以防万一解释错了。

标签: mysql sql group-by indexing where-clause


【解决方案1】:

您可以使用条件聚合在单个查询中计算两个 ID:

SELECT
  MainID,
  SubID,
  MAX(CASE WHEN Column1 <> 'ignore_me' THEN ID END) AS ID1,
  MAX(CASE WHEN Column2 <> 'ignore_me' THEN ID END) AS ID2
FROM atable
GROUP BY
  MainID,
  SubID
;

您也可以在 WHEN 条件中显式添加 AND Column<i>N</i> IS NOT NULL,但这不是必需的,NULL 值无论如何都会被忽略。

现在您可以简单地将上述子查询作为派生表进行两次左连接:

SELECT
  tm.MainID,
  tm.SubID,
  t1.Column1,
  t2.Column2
FROM (
  SELECT
    MainID,
    SubID,
    MAX(CASE WHEN Column1 <> 'ignore_me' THEN ID END) AS ID1,
    MAX(CASE WHEN Column2 <> 'ignore_me' THEN ID END) AS ID2
  FROM atable
  GROUP BY
    MainID,
    SubID
) tm
  LEFT JOIN atable t1 ON tm.ID1 = t1.ID
  LEFT JOIN atable t2 ON tm.ID2 = t2.ID
;

更新(转换为视图,响应 cmets)

到目前为止,我只能看到一种对 VIEW 友好的替代方案:

SELECT
  MainID,
  SubID,
  (
    SELECT Column1
    FROM atable
    WHERE MainID = t.MainID
      AND SubID  = t.SubID
      AND Column1 <> 'ignore_me'
    ORDER BY ID DESC
    LIMIT 1
  ) AS ID1,
  (
    SELECT Column2
    FROM atable
    WHERE MainID = t.MainID
      AND SubID  = t.SubID
      AND Column2 <> 'ignore_me'
    ORDER BY ID DESC
    LIMIT 1
  ) AS ID2
FROM atable t
GROUP BY
  MainID,
  SubID
;

不过,这个查询可能比前​​一个查询慢:它使用两个相关的子查询,而且我不确定在 MySQL 中具有相关子查询的查询(或特别是视图)是否有效。适当的索引可能会有所帮助。一般来说,您可能需要自己测试一下。

【讨论】:

  • 好的,非常感谢。这比我正在做的更好......将两个单独的查询加入到表中。知道如何在没有 FROM 子句中的子查询的情况下做到这一点吗?我希望它是一个视图。
  • 目前想不出比使用相关子查询更好的方法了。请查看我的更新。
  • 正如您所怀疑的,这两个相关的子查询在当前索引下效率不高。但是,使条件聚合查询成为视图本身并创建第二个视图是可行的。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-14
  • 1970-01-01
  • 2016-08-14
  • 2013-01-27
  • 2021-06-06
  • 1970-01-01
相关资源
最近更新 更多