为什么SQL Server中没有`select last`或`select bottom`就像`select top`?

时间:2015-05-06 09:43:24

标签: sql sql-server sql-server-2008 sql-server-2012

我知道这可能听起来像是一个愚蠢的问题,但请耐心等待。 在SQL-server中我们有

SELECT TOP N ...

现在我们可以按升序(默认情况下)获得前n行,很酷。如果我们想要在任何其他列上对记录进行排序,我们只需在order by子句中指定,就像这样......

SELECT TOP N ... ORDER BY [ColumnName]

更酷。但是如果我想要last行呢?我只是写这样的东西......

SELECT TOP N ... ORDER BY [ColumnName] DESC

但是有一点关注。我说关注而不是问题因为它实际上不是问题。通过这种方式,我可以根据该列获得最后一行,但是如果我想要插入的最后一行该怎么办。我知道SCOPE_IDENTITYIDENT_CURRENT和{ {1}},但请考虑@@IDENTITY(没有heap的表格)没有任何标识列,以及来自多个地方的多次访问(不要进入此太多关于如何以及何时发生这些多次操作,这与主要事情无关)。因此,在这种情况下,似乎没有 easy 方法来查找最后实际插入的行。有些人可能会这样回答

  

如果从[table]中选择*,则sql结果窗口中显示的最后一行将是最后一行插入的。

对于任何想到这一点的事情,实际情况并非如此,至少并非总是如此,而且您可以始终信赖( msdn,请阅读{{ 1}}部分)。

所以问题归结为这一点,就像标题本身一样。为什么SQL Server没有

clustered index

或者说

Advanced Scanning

或类似的东西,我们不必指定SELECT LAST 然后它会在执行查询时将最后一条记录插入表中(再次我不会详细介绍在未提交的读取或幻像读取的情况下如何产生这种结果。)

但是如果有的话,有人争辩说如果不谈论这些读取级别我们就不能谈论这个问题,那么,对于他们来说,我们可以使其行为与SELECT BOTTOM 工作的方式相同,但恰恰相反。但如果你的论点是,那么我们就不需要它,因为我们总能做到

Order By
那时我真的不知道该说些什么。我知道我们可以做到这一点,但有任何基于关系的原因,或某些基于语义的原因,或其他原因由于我们没有不能拥有此TOP。我不想找SELECT TOP N ... ORDER BY [ColumnName] DESC 的方法,我正在寻找原因,为什么没有它或不能拥有它。

附加 我不太了解NOSQL是如何工作的,但我已经({1}}和SELECT LAST/BOTTOM工作了(只是一点点),似乎也没有这样的东西。他们没有这个原因的原因是因为之前没有人曾经拥有它,还是因为某种原因不合理?

更新

需要知道我需要通过降序来指定顺序。在回答或评论之前,请先阅读问题并理解我的疑虑。我知道如何获得最后一排。这甚至不是问题,主要问题归结为为什么没有Order By喜欢它的对手。

更新2

VladimirPieter的答案之后,我只是想更新一下,如果我mongodb没有elastic search,我知道订单无法保证。我知道我之前在问题中所写的内容可能会让人觉得我不知道是这种情况,但如果你只是向下看,我已经给了{{3}的链接并且已经提到没有select last/bottom的{​​{1}}不保证任何排序。所以请不要在你的答案中加上我的陈述错误,因为我已经在几行之后澄清了这一点(我提供了SELECT TOP的链接)。

4 个答案:

答案 0 :(得分:14)

你可以这样想。

没有SELECT TOP N

ORDER BY会返回一些 N行,无论是第一行还是最后一行,只是某些。它未返回哪些行。您可以运行相同的语句10次,每次获得10组不同的行。

因此,如果服务器的语法为SELECT LAST N,那么这个没有ORDER BY的语句的结果将再次未定义,这正是现有SELECT TOP N没有{{1}的结果}}

你在问题​​中强调,你知道并理解我在下面写的内容,但是我仍然会保留它,以便让所有人都能在以后阅读。

问题中的第一句话

  

在SQL-server中我们现在有ORDER BY,我们可以得到   前n行按升序排列(默认情况下),很酷。

不正确。如果SELECT TOP N ...没有SELECT TOP N,您就会得到N"随机"行。好吧,不是真的随机,服务器不会故意从一行到另一行随机跳转。它选择了一些确定性的方法来扫描表格,但是可以有许多不同的方法来扫描表格,服务器可以随时根据需要更改所选择的路径。这就是" undefined"的意思。

服务器不会跟踪行插入表格的顺序,因此您再次假设ORDER BY没有SELECT TOP N的结果取决于插入行的顺序在表中不正确。

那么,你最后一个问题的答案

  

为什么没有ORDER BY喜欢它的对手。

是:

  • select last/bottom的{​​{1}}结果与ORDER BY的结果完全相同 - 未定义。
  • <{1> SELECT LAST N SELECT TOP N的结果与ORDER BY的结果完全相同。

所以,有两个关键词可以做同样的事情。

彼得的答案中有一个很好的观点:SELECT LAST N ... ORDER BY X ASC这个词有点误导。它实际上意味着SELECT TOP N ... ORDER BY X DESC结果设置为某些行。

顺便说一句,自SQL Server 2012以来,他们添加了对ANSI标准OFFSET的支持:

TOP

这里添加另一个关键词是合理的,它是ANSI标准 AND 它增加了重要的功能 - 分页,以前没有。

我要感谢@ Razort4x在这里提供了一个非常好的MSDN链接。 &#34;高级扫描&#34;有一个很好的机制示例叫做&#34;旋转木马扫描&#34;,它说明了为什么没有LIMIT无法保证从OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS } [ FETCH { FIRST | NEXT } {integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY ] 语句返回的结果的顺序子句。

这个概念经常被误解,我在这里看到了许多问题,如果他们从该链接中得到引用,那将会大大受益。

您问题的答案

  

为什么SQL Server没有SELECT或说ORDER BY或   类似的东西,我们不必指定SELECT LAST和。{1}}   然后它会给出当前插入表中的最后一条记录   执行查询(同样我不会详细说明如何   这会导致未提交的读取或幻像读取。)

是:

魔鬼在你要省略的细节中。要知道哪个记录是在执行查询时最后插入表中的&#34;&#34; (并且以某种一致/非随机的方式知道这一点)服务器需要以某种方式跟踪这些信息。即使在多个同时运行的事务的所有场景中都可能,从性能的角度来看,它很可能是昂贵的。并非每个SELECT BOTTOM都会请求此信息(实际上很少或根本没有),但跟踪此信息的开销始终存在。

所以,您可以这样想:默认情况下,服务器不会做任何特定的事情来了解/跟踪插入行的顺序,因为它会影响性能,但如果您需要知道你可以使用ORDER BY列。微软本可以设计服务器引擎,它在每个表中都需要SELECT列,但是它们是可选的,这在我看来很好。我比服务器更了解哪个表需要IDENTITY列,哪些不需要。

摘要

我想总结一下,您可以通过两种不同的方式查看IDENTITYIDENTITY

1)当您希望SELECT LAST符合现有ORDER BY时。在这种情况下,SELECT LASTSELECT TOP的结果未定义,即结果实际上是相同的。在这种情况下,它归结为(不)有另一个关键字。语言开发人员(在这种情况下是T-SQL语言)总是不愿意添加关键字,除非有充分的理由。在这种情况下,它显然是可以避免的。

2)当您希望LAST表现为TOP时。顺便说一句,这应该将相同的期望扩展到SELECT LAST以表现为SELECT LAST INSERTED ROW或添加新关键字SELECT TOPSELECT FIRST INSERTED ROW以保持现有关键字LAST_INSERTED不变。在这种情况下,它归结为性能并增加了此类行为的开销。目前,如果您不需要此信息,服务器可以避免此性能损失。如果您确实需要,FIRST_INSERTED是一个非常好的解决方案,如果您仔细使用它。

答案 1 :(得分:3)

没有选择最后,因为没有必要。考虑一下&#34;从表格#34中选择前1 * 。前1名将为您提供返回的第一行。然后过程就停止了。

但如果您没有指定订单,则无法保证订购。所以它也可能是你回来的数据集中的任何一行。

现在做一个&#34;从表格#34;中选择最后1 *。现在,数据库必须处理所有行,以便为您提供最后一行。 而且由于排序是非确定性的,它可能与选择&#34; top 1&#34;的结果相同。

现在看看问题出在哪里?没有顶部和最后的订单实际上是相同的,只有&#34;最后&#34;会花更多的时间。通过订单,实际上只需要顶部。

SELECT TOP N ...
     

现在我们可以按升序获得前n行(by   默认),很酷。如果我们想要在任何其他列上对记录进行排序,   我们只是在order by子句中指定,就像这样......

你在这里说的是完全错误的,绝对不是它的工作原理。无法保证您获得的订单。 什么上升顺序?

create table mytest(id int, id2 int)
insert into mytest(id,id2)values(1,5),(2,4),(3,3),(4,2),(5,1)
select top 1 * from mytest
select * from mytest
create clustered index myindex on mytest(id2)
select top 1 * from mytest
select * from mytest
insert into mytest(id,id2)values(6,0)
select top 1 * from mytest

逐行尝试这个代码,看看你最后的内容&#34;选择前1&#34; .....在这种情况下你会得到最后插入的记录。

<强>更新

我想你理解&#34;从表格#34中选择前1 *基本上意味着:&#34;从表中选择一个随机行&#34;。 那么最后的意思是什么? &#34;从表中选择最后一个随机行?&#34;表格中的最后一个随机行在概念上与表中的任意1个随机行相同吗?如果这是真的,那么顶部和最后都是相同的,所以没有必要持续。

更新2 事后来看,我对mysql使用的语法更加满意:LIMIT。 Top没有说出任何有关排序的信息,只是指定要返回的行数。

  

将查询结果集中返回的行限制为SQL Server 2014中指定的行数或行百分比。

答案 2 :(得分:2)

SELECT LAST_INSERTED没有意义的原因。

  1. 它不能轻易应用于非堆表。

  2. 堆数据可以由DBMS自由移动,因此这些“自然”订单可能会发生变化。为了保持它,系统需要一些额外的机制,这似乎是一种无用的浪费。

  3. 如果真的需要,可以通过添加一些“自动增量”列进行模拟。

答案 3 :(得分:0)

除非另有说明,否则SQL Server排序是任意的。它是基于集合的,因此您必须定义您的 集合。正确SCOPE_IDENTITY()是捕获最后插入记录或OUTPUT子句的正确方法。你为什么要在堆上插入需要按时间顺序引用的?这是一个超级糟糕的数据库设计。