存储顺序与结果顺序


8

这是主键中指定的排序顺序的一个衍生问题,但排序是在SELECT上执行的

@Catcall说这关于存储顺序(聚集索引)和输出顺序

许多人认为聚集索引可以保证输出的排序顺序。但这不是它的作用。它保证了磁盘上的存储顺序。 例如,请参阅此博客文章

我已经阅读了Hugo Kornelis的博客文章,并且了解到索引并不能保证sql服务器按特定顺序读取记录。但是我很难接受我不能为我的情况承担这个责任吗?

CREATE TABLE [dbo].[SensorValues](
  [DeviceId] [int] NOT NULL,
  [SensorId] [int] NOT NULL,
  [SensorValue] [int] NOT NULL,
  [Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED 
(
  [DeviceId] ASC,
  [SensorId] ASC,
  [Date] DESC
) WITH (
    FILLFACTOR=75,
    DATA_COMPRESSION = PAGE,
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF,
    IGNORE_DUP_KEY = OFF,
    ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON)
  ON [MyPartitioningScheme]([Date])

我原来的查询是这样的:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC

但我建议我也可以使用此代码(请阅读下面的说明):

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

如您所见,我的表行很小(16字节),并且只有一个索引,即一个簇。在我的情况下,该表此时包含100.000.000条记录(这很可能会增加十倍)。

当数据库服务器查询该表时,它有两种查找我的行的方法,一种是查找主键,从而读取并返回desc中的值。日期顺序,否则必须进行全表扫描。我的结论是,对所有这些记录进行全表扫描将太慢,因此数据库服务器将始终通过其主键查找表,从而返回按以下方式排序的值:Date DESC


2
为什么您希望能够如此严重地依赖此假设?为什么不只是在上面放一个ORDER BY,那您就知道可以依靠它了。参见此处的#3
亚伦·伯特兰

出于两个原因,出于好奇心,并且因为该ORDER BY子句对我来说是对性能的重大打击(有关其他信息,请参阅其他问题)。我有一个目前可以使用的解决方案,但是当我的流量增加时,它将无法解决。
m__12年

1
如果您依靠的是所看到的顺序而没有按顺序排序,那么ORDER BY不会对性能造成影响-对我而言这没有意义。
亚伦·伯特兰

4
唯一的事情保证结果集顺序是ORDER BY在查询子句。对于SQL ServerOracleMySQL和您可以想到的任何其他RDBMS都是如此。尝试其他任何方法,您将为意外的失败做好准备。
Nick Chammas 2012年

Answers:


15

让我尝试解释为什么你应该这样做,为什么你应该从来没有假设,一个SQL-产品将在一个特定的顺序返回结果集,除非你指定所以,无论指数-集群或非集群,B树或R树或kd树或分形树或DBMS使用的任何其他奇异索引。


您的原始查询告诉DBMS搜索SensorValues表,查找与3个条件匹配的行,对它们进行Date降序排列,仅保留其中的第一行,最后选择并仅返回该SensorValue列。

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC ;

这些是您已经赋予DBMS的非常具体的命令,并且每次运行查询的结果很可能是相同的(如果您有多个符合条件并具有相同条件的行,则可能不一样最大值,Date但有所不同,SensorValue但让我们在对话的其余部分假设表中不存在此类行。

为了运行此查询,DBMS是否必须执行上述操作?不,当然不是,你知道。它可能不读取表,而是从索引读取。或者,如果认为更好(更快),则可以使用两个索引。或三个。或者它可能使用缓存的结果(不是SQL Server,而是其他DBMS缓存查询结果)。或者它可能会使用并行执行一次,而不是下次运行。或...(添加影响执行和执行计划的任何其他功能)。

不过,可以保证的是,每次运行时,只要没有插入,删除或更新行,它都会返回完全相同的结果。


现在,让我们看看您的建议怎么说:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010 ;

该查询告诉DBMS搜索SensorValues表,查找与3个条件匹配的行,通过Date降序对这些行进行排序,而不关心顺序,仅保留一行,最后选择并仅返回该SensorValue列。

因此,它基本上告诉了第一个结果,除了它告诉您只需要一个符合条件的结果,而不必关心哪个

现在,我们可以假定由于聚簇索引,它总是给出相同的结果吗?
-如果确实每次都使用该聚集索引,则为是。

但是会使用它吗?
-不

为什么不?
-因为可以。查询优化器每次运行一条语句时都可以自由选择执行路径。当时认为适合该陈述的任何路径。

但是使用聚簇索引不是获取结果的最佳/最快方法吗?
-不,并非总是如此。这可能是您第一次运行查询。第二次,它可能使用缓存的结果(如果DBMS具有这种功能,则不是SQL Server *)。结果的第1000次可能已从缓存中删除,并且那里可能存在另一个结果。假设您刚刚执行以下查询:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date ASC ;         --- Notice the `ASC` here

并且缓存的结果(来自上述查询)是另一个不同的结果,它仍然符合您的条件,但不是(想要的)排序中的第一个。而且您已经告诉DBMS不要在意订单。

好的,因此只有缓存会影响此吗?
-不,还有很多其他事情。

  • 当时,DBMS认为其他索引对此查询更好。
  • 开发人员更改或完全删除了您拥有的该聚集索引。
  • 您或其他开发人员添加了另一个索引,优化器认为该索引比CI更有效。
  • 您已更新到新版本,并且新的优化器存在一个小错误或在其排名和选择执行计划的方式上有所变化。
  • 统计信息已更新。
  • 而是选择了并行执行。

*:SQL Server不会缓存查询结果,但是企业版确实具有高级扫描功能,该功能有点类似,因为并发查询可能会得到不同的结果。不确定何时启动。(谢谢@Martin Smith。)


我希望您相信,除非您指定,否则永远不要依赖SQL查询将以特定顺序返回结果。并且永远不要使用TOP (n)不带ORDER BY,除非您当然只希望结果中有n行并且您不在乎返回哪一行。


2
SQL Server Enterprise Edition确实具有高级扫描功能,该功能有点类似,因为并发查询可能会导致结果不同。不确定何时启动。
马丁·史密斯

1
可能会“随机化”结果集顺序的另一件事(即使查询显然是由有序索引驱动的)也是并行性。我看到一个启用了自动并行性的应用程序在运行有问题的SQL时开始表现不佳(不是SQL Server,但我想那可能也适用)。
2014年
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.