主键中指定的排序顺序,但在SELECT上执行排序


15

我将传感器数据存储在表SensorValues中。该表和主键如下:

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

执行计划

编辑:我可以代替吗?

由于该表已排序为DeviceId,SensorId,Date,并且我执行了仅指定一个DeviceId和一个SensorIdSELECT,因此输出集应已按Date DESC排序。因此,我想知道以下问题在所有情况下是否都能产生相同的结果?

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

根据下面的@Catcall,排序顺序与存储顺序不同。即,我们不能假设返回的值已经按排序顺序。

编辑:我已经尝试过这种交叉应用解决方案,没有运气

@Martin Smith建议我尝试将结果对分区进行外部应用。我发现了一篇博客文章(分区表上的对齐的非聚集索引)描述了类似的问题,并尝试了与Smith所建议的方案类似的解决方案。但是,这里没有运气,执行时间与我原来的解决方案相当。

WITH Boundaries(boundary_id)
AS
(
  SELECT boundary_id
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
  UNION ALL
  SELECT max(boundary_id) + 1
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
),
Top1(SensorValue)
AS
(
  SELECT TOP 1 d.SensorValue
  FROM Boundaries b
  CROSS APPLY
  (
    SELECT TOP 1 SensorValue
      FROM SensorValues
      WHERE  SensorId = 53
        AND DeviceId = 3819
        AND "Date" < 1339225010
        AND $Partition.PF(Date) = b.boundary_id
        ORDER BY Date DESC
  ) d
  ORDER BY d.Date DESC
)
SELECT SensorValue
FROM Top1

选项MAXDOP 1没有帮助。如下面@Martin Smith所指定,似乎是由分区引起的……
m__12年

Answers:


13

对于非分区表,我得到以下计划

方案1

上有一个搜寻谓词Seek Keys[1]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010

表示SQL Server可以对前两列执行相等查找,然后从1339225010并按顺序开始进行范围查找FORWARD(因为索引定义为[Date] DESC

TOP运营商将停止要求更多的行从第一行发出后寻求。

当我创建分区方案和功能时

CREATE PARTITION FUNCTION PF (int)
AS RANGE LEFT FOR VALUES (1000, 1339225009 ,1339225010 , 1339225011);
GO
CREATE PARTITION SCHEME [MyPartitioningScheme]
AS PARTITION PF
ALL TO ([PRIMARY] );

并用以下数据填充表

INSERT INTO [dbo].[SensorValues]    
/*500 rows matching date and SensorId, DeviceId predicate*/
SELECT TOP (500) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL
/*700 rows matching date but not SensorId, DeviceId predicate*/
SELECT TOP (700) 3819,52,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL 
/*1100 rows matching SensorId, DeviceId predicate but not date */
SELECT TOP (1100) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) + 1339225011      
FROM master..spt_values

SQL Server 2008上的计划如下所示。

方案2

从搜索发出的实际行数为500。计划显示查找谓词

Seek Keys[1]: Start: PtnId1000 <= 2, End: PtnId1000 >= 1, 
Seek Keys[2]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010

表明它正在使用此处描述跳过扫描方法

扩展查询优化器,以便可以对PartitionID(作为逻辑前导列)以及可能的其他索引键列执行具有一个条件的查找或扫描操作,然后可以执行具有不同条件的第二级查找在一个或多个其他列上,针对满足一级搜索操作资格的每个不同值。

该计划是一个串行计划,因此对于特定查询,您似乎认为,如果SQL Server确保以date的原始计划的降序处理分区,则该计划TOP仍将起作用,并且在第一个匹配行被删除后,它可能会停止处理。找到而不是继续输出剩下的499个匹配项。

实际上,2005年的计划看起来确实采用了这种方法

2005年计划

我不知道这是否是直截了当地得到同样的计划在2008年或也许会需要一个OUTER APPLYsys.partition_range_values进行模拟。



9

许多人认为聚集索引可以保证输出的排序顺序。但这不是它的作用。它保证了磁盘上的存储顺序。

例如,请参阅此博客文章以及此较长的讨论


1
好吧,之前,OP还说:“我会认为,因为我存储了按Date列排序的值,所以排序不会发生。” 因此,至少部分问题是对聚集索引的作用的误解。我认为还是可以理顺这一点。
Mike Sherrill'Cat Recall'

也许我只是固执(所以请原谅我;-)。无论如何,我已经阅读了Hugo Kornelis的博客文章,这很简单。但是,在他的示例中,他使用的是一个聚集索引和一个非聚集索引,该非聚集索引的大小较小,因此正在执行计划中使用。在我的情况下,我只有一个聚集索引,sql server仍然可以错误的顺序返回值(它没有较小的索引可供使用,全表扫描太慢了)?
m__12年

我已将其移至一个新问题(主题外)
m__12年

5

我猜测由于并行计划,需要SORT。我基于一些昏暗而遥远的博客文章:但是我在MSDN上发现了这一点,这可能会或可能不会证明这一点。

因此,尝试使用MAXDOP 1,看看会发生什么...

我认为,@ sql kiwi在Simple Talk上的Blog帖子中也暗示了“ Exchange运算符”。还有“ DOP依赖”


尽管我之前没有设置过分区功能date。现在,我已经并且似乎正在将分区作为2005年的罪魁祸首,对于这个特定查询来说,它可能表现得更好。
Martin Smith

1

基本上您是对的-由于主键的顺序为“ DeviceId,SensorId,Date”,因此键中的数据未按日期排序,因此无法使用。如果您的密钥以不同的顺序“ Date,DeviceId,SensorId” 排序,那么密钥中的数据将按日期排序,因此可以使用...


我已经尝试过按照您提到的方式更改密钥,所以请不要后悔。无论如何,将尝试在所有3列上创建非聚集索引,然后看看能给我带来什么。(对丢失索引的搜索仍在继续; ;-))
m__12年
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.