SQL 中 TOP 和 MAX()/MIN() 的区别

Shm*_*iel 0 sql-server aggregate query-performance

根据我的经验,如果您的数据库中有数千/数百万行,如本答案以及我在这里回答的内容和提问者的回答SELECT TOP中所示,则要快得多。SELECT MIN()MAX()

\n

我的问题是为什么,根据我的理解,TOP查看数据SELECT column FROM table与执行 a 时相同ORDER BY column,它会对结果进行排序,然后只给出行,而MIN()/MAX()函数实际上应该只查看每一行,检查该行的总和行,然后转到下一行,如果该行是><前一行,则将结果保存在函数中取决于它是否为 saMAX()或 a MIN(),而这正是ORDER BY应该做的。

\n

请不要回答,根据您的经验, 比MAX()更好TOP,因为问题实际上是MAX()/MIN()和之间有什么区别TOP?两者都查看每一行的数据,并且都可能是log(n)

\n

回复@JD:

\n
\n

一个简单的例子是,如果您使用 TOP 而不使用 ORDER BY 子句。然后,您将返回的最高结果可能是不确定的,并且并不总是必须与 MAX() 聚合函数引用的同一行。

\n
\n

当然,我只是问你何时使用ORDER BY因为它的强大TOP之处在于以特定方式对其进行排序。

\n

关于log(n)我会以不同的方式询问它是否ORDER BY\xce\xa9(n)因为它需要查看的最小数据是n并且可能有O() >= n因为它\ 可能使用最好的排序算法O(n)

\n

有了它MIN(),它也可能是\xce\xa9(n)O(n)因为它总是需要扫描所有数据来找到答案,并且永远不会超过n,所以为什么它有时会产生巨大的结果不同之处?

\n

换一种方式:

\n

如果我有一个包含一百万行的表,其中包含客户及其购买产品的金额,如果我正在使用并使用SELECT TOP 1 amount_column FROM customer_table ORDER BY amount_column DESCSELECT MAX(amount_column) FROM customer_table一个需求,那么执行时间有什么区别查看所有数据并按amount_column降序排序,这样无论是否已经排序都没有什么区别,因为它需要检查所有行是否>比上一行,然后当它执行完毕后返回最大金额。

\n

当我使用该MAX()函数执行此操作时,它还需要扫描(根据我的理解)所有行并将第一行保存到结果中,然后转到下一行并检查它是否 > 比结果(如果是)它将其保存到结果中,依此类推整个表,完成后返回最大金额。

\n

因此,根据我的理解,两者实际上都在做同样的事情,或者也许我对它执行函数的方式的理解是错误的,这就是我要问的,如果我错了?为什么?

\n

von*_*ryz 6

值得指出的是,还有另一个非常重要的区别:min()/max() 与 top() 的 null 行为不同。在这种情况下,这是苹果和橙子的比较。

考虑:

create table T1(id int, content nvarchar(64));
insert T1 values 
  (1,'smallest'), 
  (3,'largest'), 
  (2,'middle'), 
  (-1,'minus one'),
  (null, 'null');

select top(1) id from T1 order by id; -- returns null
select top(1) id from T1 order by id desc; -- returns 3
select 'max id' = max(id), 'min id' = min(id) from T1; -- returns 3 and -1
Run Code Online (Sandbox Code Playgroud)


J.D*_*.D. 5

问题是它们并不总是相同的,因此它们是具有不同目的的不同运营商。是的,其中一些目的是重叠的,但它们并不完全是一对一的。

一个简单的例子是如果您TOP不带ORDER BY子句使用。那么您将返回的最上面的结果可能是不确定的,并且并不总是必须与聚合MAX()函数引用的同一行。

正如 Mustaccio 在评论中指出的那样,执行计划将具体告诉您两个查询(一个使用 ,TOP另一个使用 )之间的操作有何不同MAX()。如果没有特定的查询,就没有关于两者如何相似或不同的单一答案,因为它可能因每个特定用例而异。

最后,假设两个运算符的时间大 O 函数始终为O(log(n))也是不正确的,并且会根据数据的结构方式以及应用这些运算符的数据点而变化。对于按升序索引的Table1列,查询的顺序可能是O(log(n))(尽管我不确定它是否可能是O(1),因为该子句与索引排序匹配)但 a可能约为O(n),因为列 上没有覆盖索引,并且可能需要扫描整个表。(A, B, C)ASELECT TOP 1 A FROM Table1 ORDER BY AORDER BYSELECT MAX(B) FROM Table1B

关于您的更新,聚合函数MIN()不需要MAX()总是扫描所有数据。我的第一个示例只是以一种方式设置,以演示一个特定用例之间的MAX()Big O 搜索时间复杂度的差异。TOP但是使用具有相同列和A仅覆盖列升序的索引的相同表示例,则查询也应产生O(log(n))SELECT MAX(A) FROM Table1的时间复杂度。

尽管只关注使用该子句的查询,但与和 之间ORDER BY的相似点或差异仍然会因一个用例而异,直接取决于正在运行的查询以及查询引擎为该查询生成的执行计划。如果您有想要讨论的特定查询和执行计划,请随时将其添加到您的问题中,我们可以对其进行适当的分析。TOPMAX()MIN()

对于您的最新更新,我相信您遇到的问题是数据结构的概念。您一直假设在这两种情况下都需要分析每一行数据,并且只有在未索引的表(堆数据结构)或查询没有覆盖索引时才如此。因此,在您的示例中,对于两个运算符,如果没有索引覆盖amount_column. 但是,如果它上面有一个索引,降序,这将尊重你的陈述“ ...如果它已经排序”,那么数据将存储在B 树数据结构中,这将允许你的查询在索引上查找并显着减少运行时间降至O(log(n)),因为不再需要比较每一行。需要比较的行子集比整个表本身小得多。

是的,在您的具体示例中,理论上,两个查询应该看到相同的搜索时间复杂度,但实际上这又取决于您的数据的结构方式(在本例中为索引)。此外,还有其他因素可能导致两个单独的查询计划,实际上两个查询之间的运行时确实不同,并且只能在比较两个实际执行计划时才能讨论其原因。理论上不可能进行讨论,因为根据执行计划本身的具体内容,它可能会因多种原因而有所不同。