【问题标题】:Best-performance query for "select max in group"?“在组中选择最大值”的最佳性能查询?
【发布时间】:2008-09-18 19:13:00
【问题描述】:

我有一个简单的表格 cmets (id INT, revision INT, comment VARCHAR(140)) 有一些内容如下:

1|1|hallo1|
1|2|hallo2|
1|3|hallo3|
2|1|hallo1|
2|2|hallo2|

我正在寻找一条 SQL 语句,它将返回每条评论的最高版本:

1|3|hallo3|
2|2|hallo2|

我想出了这个解决方案:

select id, revision, comment 
  from comments 
  where revision = (
      select max(revision) 
        from comments as f 
        where f.id = comments.id
  );

但在大型数据集上速度很慢。有没有更好的查询来完成这个?

【问题讨论】:

  • 您能否考虑重命名主题以反映优化或性能?
  • 使用窗口函数通常更快。

标签: sql database


【解决方案1】:

这是一种通过适当的索引不会非常慢并且不使用子选择的方法:

SELECT comments.ID, comments.revision, comments.comment FROM comments 
LEFT OUTER JOIN comments AS maxcomments 
ON maxcomments.ID= comments.ID
AND maxcomments.revision > comments.revision
WHERE maxcomments.revision IS NULL

改编自这里的查询: http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/

(来自谷歌搜索:max group by sql)

【讨论】:

    【解决方案2】:
    1. 确保已正确设置索引。对 id 进行索引,修订会很好。

    2. 以下是对您的查询的不同看法。还没有检查它的执行计划,但是如果你设置好索引应该会有所帮助:

      SELECT c.* 
        FROM comments c
        INNER JOIN (
              SELECT id,max(revision) AS maxrev 
                FROM comments 
                GROUP BY id
        ) b
          ON c.id=b.id AND c.revision=b.maxrev
      

    编辑添加:

    1. 如果您使用的是 SQL Server,您可能还想查看索引视图:
      http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

    再次编辑以添加信息:

    Subquery:
    25157 records
    2 seconds
    Execution plan includes an Index Seek (82%) base and a Segment (17%)
    
    Left Outer Join:
    25160 records
    3 seconds
    Execution plan includes two Index Scans @ 22% each with a Right Outer Merge at 45% and a Filter at 11%
    

    我仍然会使用子查询。

    【讨论】:

    • 如果您的执行计划试图比较接受的答案(使用左外连接)与此答案中的子查询/组查询,则您正在比较未索引的列。使用正确的索引,左外连接几乎每次都会提高性能(尤其是当您有很多记录时)。对于非常有限的记录,这个答案是完全可以接受的,但是当你达到 10K+ 记录时,你会发现使用外连接会得到更好的结果。
    【解决方案3】:

    使用我们的一张表进行了测试,该表总共有近 100 万行。字段FIELD2 和FIELD3 上都存在索引。在我们的开发盒上,查询在 3 秒内返回了 83953 行。

    select
    FIELD1, FIELD2, FIELD3
    from
    OURTABLE (nolock) T1
    WHERE FIELD3 = 
    (
    SELECT MAX(FIELD3) FROM 
    OURTABLE T2 (nolock)
    WHERE T1.FIELD2=T2.FIELD2
    )
    ORDER BY FIELD2 DESC
    

    【讨论】:

      【解决方案4】:

      我的建议是分析。

      select id, max_revision, comment
      from (select c.id, c.comment, c.revision, max(c.revision)over(partition by c.id) as max_revision
            from comments c)
      where revision = max_revision;
      

      【讨论】:

        【解决方案5】:

        来自左侧字段的想法,但是如何在表格中添加一个额外的字段:

        CurrentRevision bit not null
        

        然后,当您进行更改时,在新版本上设置标志并在所有以前的版本上删除它。

        您的查询将变成:

        select  Id,
                Comment
        from    Comments
        where   CurrentRevision = 1
        

        这在数据库上会更容易,因此会更快。

        【讨论】:

          【解决方案6】:

          执行“latest x by id”类型查询的一种非常简洁的方法是这样。它也应该很容易正确索引。

          SELECT id, revision, comment 
          FROM comments
          WHERE (id, revision) IN (
            SELECT id, MAX(revision)
            FROM comments
            -- WHERE clause comes here if needed
            GROUP BY id
          )
          

          【讨论】:

            【解决方案7】:

            对于大表我发现这个解决方案可以有更好的性能:

                SELECT c1.id, 
                       c1.revision, 
                       c1.comment 
                  FROM comments c1 
            INNER JOIN ( SELECT id, 
                            max(revision) AS max_revision
                           FROM comments 
                       GROUP BY id ) c2
                    ON c1.id = c2.id
                   AND c1.revision = c2.max_revision
            

            【讨论】:

              【解决方案8】:

              没有子选择(或临时表):

              SELECT c1.ID, c1.revision, c1.comment 
              FROM comments AS c1
              LEFT JOIN comments AS c2 
                  ON c1.ID = c2.ID
                  AND c1.revision < c2.revision
              WHERE c2.revision IS NULL
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-09-07
                • 1970-01-01
                • 2019-04-03
                • 1970-01-01
                • 1970-01-01
                • 2015-02-03
                • 1970-01-01
                相关资源
                最近更新 更多