不带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
,返回数据的顺序会随时间变化。