我知道这听起来像是一个愚蠢的问题,但请耐心等待。 在SQL服务器中,我们有
SELECT TOP N ...
现在我们可以按升序(默认情况下)获取前 n 行,很酷。如果我们希望记录在任何其他列上排序,我们只需在 order by 子句中指定,如下所示......
SELECT TOP N ... ORDER BY [ColumnName]
更酷。但是,如果我想要last
行怎么办?我只是写这样的东西...
SELECT TOP N ... ORDER BY [ColumnName] DESC
但对此有一点担忧。我说关心而不是问题,因为这实际上不是问题。这样,我可以基于该列获取最后一行,但是如果我想要插入的最后一行怎么办。我知道SCOPE_IDENTITY
,IDENT_CURRENT
和@@IDENTITY
,但考虑一个没有任何标识列的heap
(没有clustered index
的表),以及来自许多地方的多次访问(请不要过多地讨论这些多个操作是如何以及何时发生的,这与主要的事情无关)。因此,在这种情况下,似乎没有一种简单的方法来查找最后实际插入的行。有些人可能会回答这个问题
如果从 [表] 中选择 *,则 sql 结果窗口中显示的最后一行将是插入的最后一行。
对于任何考虑这一点的人,情况实际上并非如此,至少并非总是如此,并且您始终可以依赖(msdn,请阅读Advanced Scanning
部分)。
所以问题归结为这一点,就像标题本身一样。为什么 SQL Server 没有
SELECT LAST
或说
SELECT BOTTOM
或类似的东西,我们不必指定Order By
,然后它会给出在执行查询时插入表中的最后一条记录(同样,我不打算详细说明在未提交读取或幻像读取的情况下这将如何导致
)。但是,如果仍然有人争辩说,如果不谈论这些阅读级别,我们就不能谈论这一点,那么,对于他们来说,我们可以让它的行为方式与TOP
工作相同,但恰恰相反。但是如果你的论点是,那么我们不需要它,因为我们总是可以做的
SELECT TOP N ... ORDER BY [ColumnName] DESC
那我真的不知道在这里该说什么。我知道我们可以这样做,但是是否有任何基于关系的原因,或者一些基于语义的原因,或者其他一些我们没有或不能有这个SELECT LAST/BOTTOM
的原因。我不是在找办法做Order By
,我在寻找为什么没有或不能拥有它的理由。
另外,我对 NOSQL 的工作原理知之甚少,但我与mongodb
和elastic search
一起工作过(只是一点点),而且似乎也没有这样的东西。他们没有它的原因是因为以前没有人拥有过它,还是出于某种原因不合理?
更新
我不需要知道我需要按降序或不降序指定顺序。在回答或评论之前,请阅读问题并了解我的担忧。我知道我将如何获得最后一排。这甚至不是问题,主要问题归结为为什么没有像它这样的select last/bottom
。更新 2
在弗拉基米尔和彼得的回答之后,我只是想更新一下,我知道如果我在没有ORDER BY
的情况下做SELECT TOP
,订单是无法保证的。我知道从我前面在问题中写的内容可能会给人一种我不知道是这种情况的印象,但如果你再往下看,我已经给出了一个指向 msdn 的链接,并提到没有ORDER BY
的SELECT TOP
并不能保证任何排序。因此,请不要将其添加到您的答案中,因为我的陈述是错误的,因为我已经在几行之后澄清了自己(我提供了指向msdn
的链接)。
你可以这样想。
没有ORDER BY
SELECT TOP N
返回一些N
行,既不是第一行,也不是最后一行,只是一些行。未定义它返回的行。您可以运行同一语句 10 次,每次获取 10 组不同的行。
因此,如果服务器具有语法SELECT LAST N
,那么没有ORDER BY
的此语句的结果将再次未定义,这正是您在没有ORDER BY
的现有SELECT TOP N
中得到的结果。
您在问题中强调,您知道并理解我在下面写的内容,但我仍然会保留它,以便以后阅读本文的每个人都清楚。
您在问题中的第一句话
在SQL服务器中,我们现在
SELECT TOP N ...
,我们可以得到 前 n 行按升序排列(默认情况下),很酷。
不正确。使用没有ORDER BY
SELECT TOP N
,您将获得 N 个"随机"行。好吧,不是真正的随机,服务器不会故意从一行随机跳到另一行。它选择了一些确定性的方式来扫描表,但可能有许多不同的方法来扫描表,并且服务器可以在需要时自由更改所选路径。这就是"未定义"的含义。
服务器不跟踪行插入表中的顺序,因此,您认为没有ORDER BY
的SELECT TOP N
结果由表中插入行的顺序决定的假设也是不正确的。
所以,你的最后一个问题的答案
为什么没有像它一样
。select last/bottom
是:
- 如果没有
ORDER BY
SELECT LAST N
的结果将与SELECT TOP N
的结果完全相同 - 未定义。 SELECT LAST N ... ORDER BY X ASC
的结果完全相同SELECT TOP N ... ORDER BY X DESC
。
ORDER BY
的结果与因此,没有必要有两个关键字来做同样的事情。
彼得的回答有一个很好的观点:TOP
这个词有点误导。这实际上意味着LIMIT
结果设置为一定数量的行。
顺便说一下,从SQL Server 2012开始,他们增加了对ANSI标准OFFSET
的支持:
OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS } [ FETCH { FIRST | NEXT } {integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY ]
在这里添加另一个关键字是合理的,它是 ANSI 标准,它增加了重要的功能 - 分页,这在以前不存在。
我要感谢@Razort4x在他的问题中提供了指向MSDN的非常好的链接。"高级扫描"部分有一个很好的机制示例,称为"旋转木马扫描",它说明了为什么如果没有ORDER BY
子句就无法保证从SELECT
语句返回的结果的顺序。
这个概念经常被误解,我在这里看到了很多关于SO的问题,如果他们从该链接中引用,将受益匪浅。
您问题的答案
为什么 SQL Server 没有
SELECT LAST
或说SELECT BOTTOM
或 类似的东西,我们不必指定ORDER BY
和 然后它会给出当时插入表中的最后一条记录 执行查询(再次,我不打算详细介绍如何 这是否会导致未提交的读取或幻像读取)。
是:
魔鬼在你想省略的细节中。要知道哪条记录是"执行查询时最后插入表中的"(并以某种一致/非随机的方式知道这一点),服务器需要以某种方式跟踪此信息。即使在多个同时运行的事务的所有方案中都是可能的,从性能的角度来看,它很可能是昂贵的。并非每个SELECT
都会请求此信息(实际上很少或根本没有),但跟踪此信息的开销始终存在。
因此,您可以这样想:默认情况下,服务器不会执行任何特定于操作来了解/跟踪行的插入顺序,因为它会影响性能,但是如果您需要知道可以使用,例如,IDENTITY
列。Microsoft本可以设计服务器引擎,使其在每个表中都需要一个IDENTITY
列,但他们将其设置为可选,这在我看来很好。我比服务器更清楚我的哪些表需要IDENTITY
列,哪些不需要。
总结
我想总结一下,你可以用两种不同的方式看待SELECT LAST
而不ORDER BY
。
1)当您期望SELECT LAST
的行为符合现有SELECT TOP
时。在这种情况下,LAST
和TOP
的结果都是未定义的,即结果实际上是相同的。在这种情况下,它归结为(没有)另一个关键字。语言开发人员(在本例中为 T-SQL 语言)总是不愿意添加关键字,除非有充分的理由。在这种情况下,这显然是可以避免的。
2)当您期望SELECT LAST
表现得SELECT LAST INSERTED ROW
时。顺便说一下,这应该将相同的期望扩展到SELECT TOP
的行为SELECT FIRST INSERTED ROW
或添加新的关键字LAST_INSERTED
,FIRST_INSERTED
保持现有关键字TOP
不变。 在这种情况下,它归结为性能并增加了此类行为的开销。目前,如果您不需要此信息,服务器允许您避免此性能损失。如果您确实需要它IDENTITY
如果您谨慎使用它,这是一个非常好的解决方案。
没有选择最后,因为不需要它。考虑"从表中选择前 1 个 *"。Top 1 将获得返回的第一行。然后该过程停止。
但是,如果您不指定订购时间,则无法保证订购。因此,它也可能是您返回的数据集中的任何行。
现在做一个"从表中选择最后 1 个 *"。现在,数据库必须处理所有行才能获得最后一行。 而且由于排序是不确定的,因此它也可能与选择"前 1 名"的结果相同。
现在看到问题出在哪里了吗?没有按顶部和最后一个排序实际上是一样的,只有"最后一个"需要更多时间。有了订单,实际上只需要顶部。
SELECT TOP N ...
现在我们可以按升序获取前 n 行(通过 默认),很酷。如果我们希望记录在任何其他列上排序, 我们只是在 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
逐行尝试此代码,看看最后一个"选择前 1 名"会得到什么.....在这种情况下,您将获得最后插入的记录。
更新
我想您了解"从表中选择前 1 个 *"基本上意味着:"从表中随机选择一行"。 那么最后意味着什么呢?"从表中随机选择最后一行?"表中的最后一行在概念上与说表中的任意 1 个随机行不同吗?如果这是真的,顶部和最后一个是一样的,所以不需要最后一个。
更新 2事后看来,我对 mysql 使用的语法更满意:LIMIT。 Top 没有说任何关于排序的信息,它只是在那里指定要返回的行数。
将查询结果集中返回的行限制为 SQL Server 2014 中的指定行数或行百分比。
SELECT LAST_INSERTED
的原因没有意义。
-
它不能轻易地应用于非堆表。
-
堆数据可以通过DBMS自由移动,因此这些"自然"顺序可能会发生变化。为了保持它,系统需要一些额外的机制,这似乎是一种无用的浪费。
-
如果确实需要,可以通过添加一些"自动增量"列来模拟。
除非另有说明,否则SQL Server排序是任意的。它是基于集合的,因此您必须定义您的集合是什么。正确的SCOPE_IDENTITY()
是捕获上次插入的记录或OUTPUT
子句的正确方法。为什么要在需要按时间顺序引用的堆上进行插入?这是非常糟糕的数据库设计。