У меня есть одна таблица с именем Item , в ней около 80 миллионов строк:
DDL :
CREATE TABLE [dbo].[Item](
[ItemID] [int] NOT NULL,
[GroupID] [char](36) NULL,
[KeyName] [varchar](50) NOT NULL,
[KeyValue] [varchar](50) NOT NULL,
CONSTRAINT [PK_Item_User] PRIMARY KEY CLUSTERED
(
[ItemID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20170605-101521] ON [dbo].[Item]
(
[GroupID] ASC
)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 [PRIMARY]
CREATE NONCLUSTERED INDEX [Key_1] ON [dbo].[Item]
(
[KeyName] ASC
)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 [PRIMARY]
Другая таблица с именем Order , в нем около десяти миллионов строк.
DDL :
CREATE TABLE [dbo].[Order](
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[GroupID] [char](36) NOT NULL,
[OrderNr] [varchar](512) NOT NULL,
[ItemID] [int] NOT NULL,
[Memo] [varchar](512) NOT NULL,
[ServiceType] [char] NOT NULL,
CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED
(
[OrderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [NonClusteredIndex-Order_OrderNum] ON [dbo].[Order]
(
[OrderNr] ASC,
[ItemID] ASC
)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 [PRIMARY]
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20191231-143827] ON [dbo].[Order]
(
[GroupID] ASC
)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 [PRIMARY]
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20191231-143949] ON [dbo].[Order]
(
[ItemID] ASC
)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 [PRIMARY]
При выполнении ниже SQL в моей реальной среде:
select
(select top 1 KeyValue from Item i where i.ItemID=o.ItemID) KeyValue, -- i.ItemID is primary key with Cluster index default
o.Memo
from [dbo].[Order] o
where ServiceType in ('X', '*') and OrderNr not like '9%' and OrderNr not like '54%';
Это занимает более 2 минут.
Ниже приводится фактический план выполнения ( Индексное сканирование ):
При выполнении ниже SQL:
select
(select top 1 KeyValue from Item i where i.ItemID=o.ItemID and i.GroupID=o.GroupID) KeyValue, --i.ItemID is cluster index and i.GroupID is non cluster index
o.Memo
from [dbo].[Order] o
where ServiceType in ('X', '*') and OrderNr not like '9%' and OrderNr not like '54%';
Это занимает менее 10 секунд в моей реальной среде.
Ниже приведен фактический план выполнения ( Поиск индекса ):
Из двух вышеуказанных планов вы увидите:
Первый SQL использует только индекс кластера (Item. ItemID), но SQL выполняется Index Scan вместо Index Seek , его Rows Read составляет 80M.
Второй SQL использует кластерный индекс (Item.ItemID) и некластерный индекс (Item.GroupID), но SQL выполняется Index Seek , его Rows Read всего 8M.
Версия сервера SQL в моей реальной среде 11.0.7462.6 .
И я создал одну MS SQL скрипку и сгенерировал 10К тестовых записей, пытаясь воспроизвести проблему. Но, к сожалению, обе команды SQL в скрипте, похоже, выполняются с одним и тем же планом, поведение в моей реальной среде отличается.
Кто-нибудь знает, почему оптимизатор использует сканирование индекса при первом выполнении SQL? Почему добавление одного некластерного индекса (Item.GroupID) может запустить его в Поиск индекса и значительно повысить производительность?
Спасибо.