Оптимизация запросов SQL путем удаления оператора сортировки в плане выполнения - PullRequest
22 голосов
/ 14 мая 2011

Я только начал изучать оптимизацию моих запросов с помощью индексов, потому что данные SQL растут большими и быстрыми. Я посмотрел на то, как оптимизатор обрабатывает мой запрос с помощью плана выполнения в SSMS, и заметил, что используется оператор сортировки. Я слышал, что оператор Sort указывает на неверный дизайн в запросе, поскольку сортировка может быть выполнена преждевременно с помощью индекса. Итак, вот пример таблицы и данных, похожих на то, что я делаю:

IF OBJECT_ID('dbo.Store') IS NOT NULL DROP TABLE dbo.[Store]
GO

CREATE TABLE dbo.[Store]
(
    [StoreId] int NOT NULL IDENTITY (1, 1),
    [ParentStoreId] int NULL,
    [Type] int NULL,
    [Phone] char(10) NULL,
    PRIMARY KEY ([StoreId])
)

INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '2223334444')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '3334445555')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '0001112222')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '1112223333')
GO

Вот пример запроса:

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND ([Type] = 0 OR [Type] = 1)
ORDER BY [Phone]

Я создаю некластеризованный индекс, чтобы ускорить запрос:

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

Чтобы построить индекс IX_Store, я начну с простых предикатов

[ParentStoreId] = 10
AND ([Type] = 0 OR [Type] = 1)

Затем я добавляю столбец [Phone] для ORDER BY и закрываю вывод SELECT

Таким образом, даже когда индекс создается, оптимизатор все еще использует оператор сортировки (а не сортировку индекса), поскольку [Phone] сортируется ПОСЛЕ [ParentStoreId] И [Type]. Если я удаляю столбец [Type] из индекса и запускаю запрос:

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
--AND ([Type] = 0 OR [Type] = 1)
ORDER BY [Phone]

Тогда, конечно, оператор сортировки не используется оптимизатором, поскольку [Phone] сортируется по [ParentStoreId].

Итак, вопрос в том, как мне создать индекс, который будет охватывать запрос (включая предикат [Type]) и не позволить оптимизатору использовать сортировку?

EDIT:

Таблица, с которой я работаю, содержит более 20 миллионов строк

1 Ответ

17 голосов
/ 14 мая 2011

Во-первых, вы должны убедиться, что сортировка на самом деле является узким местом производительности.Продолжительность сортировки будет зависеть от количества сортируемых элементов, а количество хранилищ для конкретного родительского хранилища, вероятно, будет небольшим.(Это предполагает, что оператор сортировки применяется после применения предложения where).

Я слышал, что оператор Sort указывает на неверный дизайн в запросе, поскольку сортировка может быть выполнена преждевременно через индекс

Это чрезмерное обобщение.Зачастую оператор сортировки может быть просто перемещен в индекс, и, если извлекаются только первые несколько строк набора результатов, это может существенно снизить стоимость запроса, поскольку базе данных больше не нужно извлекать все совпадающие строки (и сортировать ихвсе), чтобы найти первые, но может читать записи в порядке набора результатов и останавливаться, как только будет найдено достаточное количество записей.

В вашем случае вы, кажется, извлекаете весь набор результатов, поэтому сортировка выполняется так:вряд ли все ухудшится (если только набор результатов не будет огромным).Кроме того, в вашем случае может быть нетривиально создать полезный отсортированный индекс, потому что предложение where содержит или.

Теперь, если вы все еще хотите избавиться от этого оператора сортировки, вы можете попробовать:

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] in (0, 1)
ORDER BY [Phone]    

В качестве альтернативы вы можете попробовать следующий индекс:

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Phone], [Type])

, чтобы попытаться заставить оптимизатор запросов выполнить сканирование диапазона индекса только на ParentStoreId, а затем просканировать все соответствующие строкив индексе, выводя их, если Type соответствует.Однако это может вызвать увеличение дискового ввода-вывода и, следовательно, замедлить ваш запрос, а не ускорить его.

Редактировать : В качестве последнего средства вы можете использовать

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] = 0
ORDER BY [Phone]

UNION ALL

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] = 1
ORDER BY [Phone]

с помощью

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

и сортируйте два списка на сервере приложений, где вы можете объединить (как в сортировке слиянием) предварительно отсортированные списки, тем самым избегая полной сортировки.Но это действительно микрооптимизация, которая, хотя и ускоряет саму сортировку на порядок, вряд ли сильно повлияет на общее время выполнения запроса, так как я ожидаю, что узким местом будет сетевой и дисковый ввод-вывод,особенно в свете того факта, что диск будет выполнять много произвольного доступа, поскольку индекс не кластеризован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...