【问题标题】:SQL performance MAX()SQL 性能 MAX()
【发布时间】:2012-07-02 09:04:47
【问题描述】:

刚刚收到一个小问题。尝试获取表的单个最大值时。哪个更好?

SELECT MAX(id) FROM myTable WHERE (whatever)

SELECT TOP 1 id FROM myTable WHERE (whatever) ORDER BY id DESC

我使用的是 Microsoft SQL Server 2012

【问题讨论】:

  • 你测试了吗?如果优化器好的话,我希望它们是一样的。
  • 如果id 是自动递增的,则此问题与stackoverflow.com/questions/590079/… 重复
  • id 代表任何类型的任何列
  • 最终归结为索引而不是类型。

标签: sql sql-server performance tsql


【解决方案1】:

没有区别,因为您可以通过检查执行计划来测试自己。如果id 是聚集索引,您应该会看到有序聚集索引扫描;如果它没有被索引,您仍然会看到表扫描或聚集索引扫描,但在这两种情况下都不会被排序。

如果您想从行中提取其他值,TOP 1 方法可能很有用,这比在子查询中提取最大值然后加入要容易。如果您想要该行中的其他值,您需要指定如何处理这两种情况下的平局。

话虽如此,但在某些情况下计划可能会有所不同,因此根据列是否被索引以及是否单调增加来测试很重要。我创建了一个简单的表并插入了 50000 行:

CREATE TABLE dbo.x
(
  a INT, b INT, c INT, d INT, 
  e DATETIME, f DATETIME, g DATETIME, h DATETIME
);
CREATE UNIQUE CLUSTERED INDEX a ON dbo.x(a);
CREATE INDEX b ON dbo.x(b)
CREATE INDEX e ON dbo.x(e);
CREATE INDEX f ON dbo.x(f);

INSERT dbo.x(a, b, c, d, e, f, g, h)
SELECT 
  n.rn, -- ints monotonically increasing
  n.a,  -- ints in random order
  n.rn, 
  n.a, 
  DATEADD(DAY, n.rn/100, '20100101'), -- dates monotonically increasing
  DATEADD(DAY, -n.a % 1000, '20120101'),     -- dates in random order
  DATEADD(DAY, n.rn/100, '20100101'),
  DATEADD(DAY, -n.a % 1000, '20120101')
FROM
(
  SELECT TOP (50000) 
     (ABS(s1.[object_id]) % 10000) + 1, 
     rn = ROW_NUMBER() OVER (ORDER BY s2.[object_id])
  FROM sys.all_objects AS s1 
  CROSS JOIN sys.all_objects AS s2
) AS n(a,rn);
GO

在我的系统上,这在 a/c 中创建了 1 到 50000 的值,b/d 在 3 到 9994 之间,e/g 从 2010-01-01 到 2011-05-16,f/h 从 2009-04 -28 至 2012-01-01。

首先,让我们比较索引单调递增的整数列 a 和 c。 a 有聚集索引,c 没有:

SELECT MAX(a) FROM dbo.x;
SELECT TOP (1) a FROM dbo.x ORDER BY a DESC;

SELECT MAX(c) FROM dbo.x;
SELECT TOP (1) c FROM dbo.x ORDER BY c DESC;

结果:

第四个查询的最大问题是,与MAX 不同,它需要排序。这是 3 与 4 的对比:

这将是所有这些查询变体中的一个常见问题:针对未索引列的 MAX 将能够搭载聚集索引扫描并执行流聚合,而 TOP 1 需要执行排序会更贵。

我进行了测试,并在测试 b+d、e+g 和 f+h 中看到了完全相同的结果。

所以在我看来,除了生成更多符合标准的代码之外,使用 MAX 而不是 TOP 1 还具有潜在的性能优势,具体取决于基础表和索引(在您使用之后可能会发生变化) '已将您的代码投入生产)。所以我想说,如果没有更多信息,MAX 更可取。

(正如我之前所说,TOP 1 可能真的是你所追求的行为,如果你要拉更多的列。你会想要测试 MAX + JOIN 方法,如果那是什么你在追求。)

【讨论】:

  • +1 - 但是是否有任何 SQL 用于计算您的百分比,或者您是否询问了执行计划的 XML?很高兴在您的回答中包含这一点,以便未来的读者也了解如何自己审问这些计划。
  • 刚刚显示了来自我公司的免费工具 SQL Sentry Plan Explorer 的相关输出。 sqlsentry.net
  • A good article on Top N sort here. 如果有人感兴趣的话。它不必实际对所有行进行排序(只需要跟踪TOP 1),但与流聚合不同,它确实需要内存授予。
  • 虽然我怀疑 TOP 1 排序运算符在计划中的成本过高。我尝试了TOP 1TOP 100TOP 101,尽管最后一个需要对所有行进行排序,但它们都给了我相同的估计子树成本。
  • 这确实是一个很好且全面的答案。如果可以的话,我会接受两次。谢谢
【解决方案2】:

第一个的意图肯定更清楚。

对于这个特定的查询不应该有显着的性能差异(它们实际上应该几乎相同,即使在myTable 中没有行时结果是不同的)。除非您有充分的理由调整查询(例如已证实的性能问题),否则请始终选择能显示代码意图的查询。

【讨论】:

  • 另一个优点是第一个查询也与 DBMS 无关,这意味着您可以采用相同的查询并在几乎任何其他 DBMS 上运行它,它仍然可以工作,而第二个查询使用SQL-Server 特定的 TOP 关键字,仅受 SQL-Server 支持。
【解决方案3】:

所有称职的查询优化器都应该为两个查询生成具有相同性能的查询计划:如果正在优化的列上有索引,则两个查询都应该使用它;如果没有索引,两者都会产生全表扫描。

【讨论】:

    【解决方案4】:

    虽然我怀疑 TOP 1 排序运算符在计划中的成本过高。我尝试了 TOP 1、TOP 100 和 > 和 TOP 101,尽管最后一个 > 需要对所有行进行排序,但它们都给了我相同的估计子树成本。 – 马丁·史密斯 7 月 2 日 6:53

    无论您需要 1 行还是 100 行,优化器都必须在此示例中执行相同数量的工作,即从表中读取所有行(聚集索引扫描)。然后对所有这些行进行排序(排序操作),因为没有C 列的索引。最后只显示需要哪一个。

    SELECT TOP (1) b FROM dbo.x ORDER BY b DESC
    option(recompile); 
    SELECT TOP (100) b FROM dbo.x ORDER BY b DESC
    option(recompile); 
    

    尝试上面的代码,这里前 1 和前 100 显示差异成本,因为 b 列上有一个索引。因此,在这种情况下,您不需要读取所有行并对它们进行排序,但工作是转到最后一页指针。对于一行,读取索引最后一页上的最后一行。 TFor 100 row 找到最后一页的最后一行,然后开始向后扫描,直到得到 100 行。

    【讨论】:

    • 这是不正确的。请阅读the link I gave which explains how the TOP N sort works。 100 是不同方法之间的一个神奇数字,但看起来计划中的成本计算并未考虑到这一点,因为在针对 Aaron 的演示数据运行时,TOP 1TOP 50000 的成本相同。对于TOP 1,它基本上只需要跟踪最大值,这与MAX 需要做的相同,尽管它的实现方式不同。它不需要对所有 50,000 行进行排序。
    • 如果没有排序 50000 行,如果列表没有排序,你怎么知道哪个是最大值。 C 列没有索引。
    • 通过扫描所有这些并将每个与您目前看到的TOP 1 值进行比较。无需将整组 50,000 行排序。
    • 不需要将位置 2 到 50,000 的所有行排序。我们不在乎那些。 TOP 1 不会这样做,但 TOP 101 会。这在我链接的帖子中很清楚。然而,尽管TOP 1 的工作量明显减少,因此估计的子树成本并没有显示出任何成本差异。
    • 成本将相同。扫描所有行一次并记住迄今为止看到的最高行具有O(n) 复杂性。对所有行进行排序具有O(n log n) 复杂性。
    猜你喜欢
    • 2019-11-01
    • 2017-10-12
    • 1970-01-01
    • 2011-05-21
    • 2018-02-08
    • 2020-08-20
    • 1970-01-01
    • 2015-11-07
    • 2016-03-18
    相关资源
    最近更新 更多