Почему первичный (кластерный) ключ не используется в этом запросе?

10

У меня есть таблица SQL Server 2008 R2, структура схемы выглядит следующим образом:

CREATE TABLE [dbo].[CDSIM_BE]
(
    [ID] [bigint] NOT NULL,
    [EquipmentID] [varchar](50) NOT NULL,
    [SerialNumber] [varchar](50) NULL,
    [PyrID] [varchar](50) NULL,
    [MeasMode] [varchar](50) NULL,
    [ReadTime] [datetime] NOT NULL,
    [SubID] [varchar](15) NULL,
    [ProbePosition] [float] NULL,
    [DataPoint] [int] NULL,

    CONSTRAINT [PK_CDSIM_BE] 
    PRIMARY KEY CLUSTERED ([ID] ASC, [EquipmentID] ASC, [ReadTime] ASC)
         WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
               IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
               ALLOW_PAGE_LOCKS = ON) ON [MonthlyArchiveScheme9]([ReadTime])
) ON [MonthlyArchiveScheme9]([ReadTime])

CREATE NONCLUSTERED INDEX [idx_CDSIM_BE__SubID_ProbePosition] 
ON [dbo].[CDSIM_BE] ([SubID] ASC, [ProbePosition] ASC)
INCLUDE ([EquipmentID], [ReadTime], [BECorr]) 
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
          SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
          ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [MonthlyArchiveScheme9]([ReadTime])

CREATE NONCLUSTERED INDEX [IX_CDSIM_BE_ProbePosition] 
ON [dbo].[CDSIM_BE] ([ProbePosition] ASC)
INCLUDE ([SerialNumber], [SubID]) 
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
          SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
          ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [MonthlyArchiveScheme9]([ReadTime])

CREATE NONCLUSTERED INDEX [IX_CSDIM_Readtime] 
ON [dbo].[CDSIM_BE]([ReadTime] ASC)
INCLUDE ([EquipmentID]) 
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
          SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
          ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [MonthlyArchiveScheme9]([ReadTime])

И я выполняю этот простой запрос:

Select Max(Id)
From dbo.CDSIM_BE

В таблице ~ 2,5В строк.

План запроса показывает сканирование индекса, выполняемое для IX_CdSIM_BE_ProbePositionиндекса. Мне интересно, почему SQL Server просто не будет использовать кластерный (и основной) индекс и сразу же перейдет к последней строке таблицы и получит значение Id, поскольку оно должно быть максимальным.

Рэнди Миндер
источник
Макс () и мин () агрегаты часто являются проблематичными в этом отношении. Если вы хотите быть уверенным, что используется индекс, напишите вместо этогоselect top 1 Id from dbo.CDSIM_BE order by Id descending;
Pieter Geerkens
4
Кластерный индекс разделен на части, ReadTimeпоэтому он не может использовать PK, как вы описываете. Необходимо найти Max(Id)для каждого раздела, а затем найти максимум из них. Можно переписать запрос, чтобы получить такой план, как указано
Мартин Смит,

Ответы:

7

Кластерный индекс разделен на части, ReadTimeпоэтому он не может использовать PK, как вы описываете. Необходимо найти Max(Id)для каждого раздела, а затем найти максимум из них. Это есть возможность переписать запрос , чтобы получить такой план , однако.

Используя пример, основанный на статье здесь, возможное переписывание может быть

SELECT MAX(ID) AS ID
FROM   sys.partitions AS P
       CROSS APPLY (SELECT MAX(ID) AS ID
                    FROM   [dbo].[CDSIM_BE]
                    WHERE  $PARTITION.MonthlyArchiveFunction9(ReadTime) 
                                                    = P.partition_number) AS A
WHERE  P.object_id = OBJECT_ID('dbo.CDSIM_BE')
       AND P.index_id <= 1; 

Обрабатывать каждый раздел по очереди.

Обратите внимание, что план все еще имеет сканирование (с предикатом поиска для выбора раздела), но это не полное сканирование раздела.

Сканирование выполняется в индексном порядке с направлением «НАЗАД». TOPИтератора может остановить запрос строки из сканирования после того, как первый из них получен.

введите описание изображения здесь

Мартин Смит
источник