NOLOCK提示更改返回记录的顺序


11

在表Client字段上有一个聚集索引LastName

当我简单地转储表中的所有记录时,除非按(nolock)提示使用提示,否则它们将按字母顺序显示。该提示会更改记录的顺序。应该是?。我敢肯定,没有其他会话具有对该表所做的更改的开放事务(至少sp_who2没有显示任何内容)。

如何解释顺序上的差异?

从评论中提取的其他信息:

  1. 没有命令。非聚集索引是否应强制执行该命令?

  2. 即使使用指定聚簇索引的索引提示,查询仍然返回不同的顺序。应该吗 我想知道为什么nolock在不明显更改计划的情况下更改返回记录的顺序。

  3. 我对它们做了一个WinDiff-除了(nolock)[查询提示] 之外,其他都一样。


21
提示不会更改顺序-因为没有可以更改的顺序
a_horse_with_no_name

Answers:


47

不带ORDER BY子句的有序结果集的外观通常是由扫描以索引顺序检索行导致的。通常在默认READ COMMITTED隔离级别下选择索引顺序扫描的一个原因是,它减少了不必要的并发异常的机会,例如多次遇到同一行或完全跳过某些行。这在多个地方都有详细介绍,包括本系列有关隔离级别的文章

通过NOLOCK表提示,可以放宽此行为,并以更宽容的READ UNCOMMITTED隔离级别执行对表的访问,隔离级别可以按分配顺序而不是索引顺序扫描数据。如该链接中所述,有关是否使用分配顺序扫描或索引顺序扫描的决定由存储引擎决定。执行之间可以更改此选择,而无需更改查询计划

这听起来可能很抽象,但是使用针对AdventureWorks2012数据库的未记录函数,通过某些查询可以更轻松地证明这一点。

USE AdventureWorks2012;
GO
-- Appears to be ordered by BusinessEntityID
-- File:Page:Slot goes up and down several times
-- Show physical locations with sys.fn_PhysLocFormatter (undocumented)
SELECT
    P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P;

-- Same query with TABLOCK or NOLOCK
-- Allocation-order (IAM) scan
-- Now appears to be ordered by File:Page:Slot instead of BusinessEntityID
SELECT P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P WITH (NOLOCK);

查询结果

这些查询是从Paul White借来的,稍作修改。

最后,为了清楚起见,此答案与有序结果集的外观有关。没有顶级域名,就不能保证展示顺序ORDER BY

分配顺序扫描可能会在多种其他情况下发生,例如,当获取表级锁或数据库处于只读模式时。并行性也会影响返回数据的顺序。关键是没有设计ORDER BY,返回数据的顺序会随时间变化。


7
很好,比我未曾尝试过的要具体得多。关键是在最后两段中。最后,“为什么”并不重要。也许您会比我更幸运地敲起那个和弦。
亚伦·伯特兰

12

当我简单地转储表中的所有记录时

...那么您不应期望有任何订单。实际上,同一查询多次运行可能会以不同顺序返回而不会发出警告。原因是您的两个查询(最可能是“不同的”查询是由于查询文本不同,而不是由于提示)具有不同的执行计划(而且差别可能很细微,例如在内部看起来相同的迭代器)两个计划,但ordered: true只有一个;或者估计行的数量大不相同,因为其中一个是在重大数据更改之前进行编译的)。正如詹姆斯在回答中正确概述的那样,也可能正在进行分配顺序扫描。

无论如何,由于运行的查询不带ORDER BY,SQL Server推断您不在乎顺序,因此关闭并确定返回行的最有效方式(如果您不关心顺序,则不在乎顺序)。

让我说得很清楚:

如果您希望或期望某个顺序,请添加ORDER BY子句

在此之前:

我已经在博客上写了:

这是后者的引文,重申了我说过的话,以解决您对使用索引提示而不是ORDER BY从句的评论:

即使在使用暗示索引的情况下,也将使用索引(如果可能,则在大多数情况下会出现错误),但是仅因为使用了索引并不意味着结果将按键中的键排序该索引。您仍然需要一个,ORDER BY以确保按索引键进行排序。

Conor Cunningham-一个非常聪明的人,直接负责SQL Server在为您处理查询时所做的大部分工作-在这里也写了有关此内容的博客:

请阅读一些内容,然后ORDER BY在抱怨不同或意外的排序之前在查询中添加一个子句。


11

James很好地解释了它是如何工作的,但我只想重申一件事:除非使用排序函数,否则结果集中的行顺序是不确定的。如果需要给定的排序,请使用显式order by子句-如果未指定,则基本上是在说“我根本不在乎该顺序”,而不是“通过聚簇索引对其进行排序”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.