Почему этот запрос не использует правильный индекс? - PullRequest
3 голосов
/ 12 мая 2011

Определение таблицы:

CREATE TABLE [dbo].[AllErrors](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [DomainLogin] [nvarchar](50) NULL,
  [ExceptionDate] [datetime] NULL,
  [ExceptionDescr] [nvarchar](max) NULL,
  [MarketName] [nvarchar](50) NULL,
  [Version] [nvarchar](50) NULL,
  CONSTRAINT [PK_AllErrors] PRIMARY KEY CLUSTERED ([ID] ASC)
)

-- Add an index on the date
CREATE NONCLUSTERED INDEX [IX_ExceptionDate] ON [dbo].[AllErrors] ([ExceptionDate] ASC)

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

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1

enter image description here

Этот код не использует мой IX_ExceptionDate (как почерпнуто)из плана выполнения).Он выполняет кластерное сканирование по индексу первичного ключа.Однако приведенный ниже код использует индекс IX_ExceptionDate:

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1
  AND ExceptionDate = ExceptionDate

enter image description here

Почему это так?

РЕДАКТИРОВАТЬ: добавлен визуальный план выполнения.

РЕДАКТИРОВАТЬ: текстовые планы выполнения ниже.

Запрос 1:

| - Вставка таблицы (OBJECT: ([# yst]), SET: ([# yst]. [ID] = [Expr1006], [# yst]. [DomainLogin] = [MarketStats]. [Dbo]. [AllErrors]. [DomainLogin], [# yst]. [ExceptionDate] = [MarketStats]. [Dbo]. [AllErrors]. [ExceptionDate], [# yst]. [ExceptionDescr] = [MarketStats]. [Dbo]. [AllErrors]. [ExceptionDescr], [# yst]. [MarketName] = [MarketStats]. [Dbo]. [AllErrors]. [MarketName], [# yst]. [Version] = [MarketStats]. [Dbo]. [AllErrors]. [Version])) | --Top (ROWCOUNT est 0) | --Compute Scalar(DEFINE: ([Expr1006] = setidentity ([MarketStats]. [Dbo]. [AllErrors]. [ID], (- 7), (0), N '# yst'))) | - Сканирование кластерного индекса (ОБЪЕКТ: ([MarketStats]. [Dbo]. [AllErrors]. [PK_AllErrors]), ГДЕ: ([MarketStats]. [Dbo]. [AllErrors]. [ExceptionDate]> = [@ вчера] И [MarketStats]. [DBO]. [AllErrors]. [ExceptionDate] <= [@ вчера] + '1900-01-02 00: 00: 00.000 ')) </p>

Запрос 2:

| - Вставка таблицы (OBJECT: ([# yst]), SET :([#yst]. [ID] = [Expr1006], [# yst]. [DomainLogin] = [MarketStats]. [dbo]. [AllErrors]. [DomainLogin], [# yst]. [ExceptionDate] = [MarketStats]. [dbo]. [AllErrors]. [ExceptionDate], [# yst]. [ExceptionDescr] = [MarketStats]. [dbo]. [AllErrors]. [ExceptionDescr], [# yst]. [MarketName] = [MarketStats]. [dbo]. [AllErrors]. [MarketName], [# yst]. [Version] = [MarketStats]. [dbo]. [AllErrors]. [Version])) | --Top (ROWCOUNT est 0) | --Compute Scalar (DEFINE: ([Expr1006] = setidentity ([MarketStats]. [Dbo]. [AllErrors]. [ID], (- 7), (0), N '# yst'))) | --NestedЦиклы (Внутреннее объединение, НАРУЖНЫЕ ССЫЛКИ: ([MarketStats]. [Dbo]. [AllErrors]. [ID], [Expr1008]), ОПТИМИЗИРОВАННЫЕ С НЕПРАВИЛЬНЫМ ПРЕДВАРИТЕЛЬНЫМ ПРЕОБРАЗОВАНИЕМ | | - Индексный поиск (OBJECT: ([MarketStats]. [Dbo]). [AllErrors]. [IX_ExceptionDate]), SEEK: ([MarketStats]. [Dbo]. [AllErrors]. [ExceptionDate]> = [@yesterday] И [MarketStats]. [Dbo]. [AllErrors]. [ExceptionDate]<= [@yesterday] + '1900-01-02 00: 00: 00.000'), ГДЕ: ([MarketStats].[dbo]. [AllErrors]. [ExceptionDate] = [MarketStats]. [dbo]. [AllErrors]. [ExceptionDate]) ORDERED FORWARD) | - Поиск кластерного индекса (OBJECT: ([MarketStats]. [dbo]. [AllErrors]. [PK_AllErrors]), SEEK: ([MarketStats]. [Dbo]. [AllErrors]. [ID] = [MarketStats]. [Dbo]. [AllErrors]. [ID]) СМОТРЕТЬ ЗАКАЗАТЬ ВПЕРЕД) </p>

Ответы [ 2 ]

6 голосов
/ 12 мая 2011

Он не знает, каким будет значение переменных, когда он компилирует запрос.Вы можете попробовать OPTION (RECOMPILE).

Я предполагаю, что добавление в запрос предложения AND (хотя логически это вообще не делает его более избирательным) должно ввести в заблуждение оптимизатора в оценке запроса с большей избирательностью, что даст вам план, которыйВы хотели!

Вы говорите в комментариях, что версия без ExceptionDate = ExceptionDate оценивается в 88234.8 строк, а версия с 8823.48

Обычно при отсутствии полезной статистики SQLСервер возвращается к эвристике, зависящей от типа оператора сравнения в предикате.

Предполагается, что предикат > вернет, например, 30% строк, а предикат = вернет 10%.из строк, так что, похоже, он просто применяет это непосредственно к результату первой оценки.Интересно, что здесь не учитывается тот факт, что здесь равен равен сам столбец!

cf Рекомендации по управлению статистикой - избегайте использования локальных переменных в запросах

5 голосов
/ 12 мая 2011

Краткий ответ: из-за «SELECT *» ваш запрос попадает в кластеризованный индекс: операция поиска ключа намного дороже, чем сканирование кластерного индекса.

См. Различные планы запросов, полученные из

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO dbo.#yst
from AllErrors WITH (INDEX = IX_ExceptionDate)
where ExceptionDate between @yesterday and @yesterday + 1

И

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1

И

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT ExceptionDate INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...