Как запрос с ТОП 10 может занять бесконечно больше времени, чем тот же запрос без ТОП 10? - PullRequest
9 голосов
/ 24 ноября 2011

У меня очень простая ситуация:

У меня есть табличная функция с именем FullTextPagina, определенная следующим образом:

select * from Pagina as p where contains(p.PageText, @term)

И затем у меня есть 2 запроса:

declare @term nvarchar(4000)= N'"DIEGO NUNES J COMBINADO"'

SELECT Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC

SELECT TOP 10 Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC

Они идентичны, за исключением того факта, что второй содержит оператор TOP 10.И они ничего не возвращают.0 строк.

Первый выполняется мгновенно.Секунд занимает 1: 20 м.

Почему?

PS:

  1. Я правильно настроил полнотекстовый индекс
  2. У меня есть некластеризованный неуникальный нисходящий индекс для DtPagina
  3. План выполнения находится здесь: http://i.imgur.com/77vJB.png

РЕДАКТИРОВАТЬ

Любопытно, что в ответ на @MartinSmith «Число выполнений» для функций с табличными значениями составляет 1,18 миллиона для случая TOP 10 и 1 для другого случая

РЕДАКТИРОВАТЬ 2

План выполнения XML http://tecnologia.novaprolink.com.br/Execution%20plan.xml

РЕДАКТИРОВАТЬ 3

Добавление опции (перекомпиляция) или удаление параметров невлияет на результат

SELECT Id, DtPagina
FROM FullTextPagina(N'"DIEGO NUNES J COMBINADO"')
ORDER BY DtPagina DESC

SELECT TOP 10 Id, DtPagina
FROM FullTextPagina(N'"DIEGO NUNES J COMBINADO"')
ORDER BY DtPagina DESC
OPTION (RECOMPILE)

РЕДАКТИРОВАТЬ 4

Полный код для FullTextPagina

USE [RexConsumo_2011_11]
GO

/****** Object:  UserDefinedFunction [dbo].[FullTextPagina]    Script Date: 11/24/2011 11:43:09 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[FullTextPagina] (@term nvarchar(4000))
RETURNS TABLE 
AS
RETURN 
(
    select * from Pagina as p where contains(p.PageText, @term)
)

GO

1 Ответ

5 голосов
/ 24 ноября 2011

Проблема, которую вы получаете, заключается в том, что SQL Server точно не определяет, сколько строк будет соответствовать предикату.

Ваш запрос делает SELECT TOP 10 Id, DtPagina ... ORDER BY DtPagina DESC.Есть несколько вариантов того, как это можно сделать

Опция 1

Он может просто сканировать индекс DtPagina DESC по порядку и посмотреть, соответствует ли каждая строка полнотекстовому предикату, а затем выйтикогда найдены первые 10 в порядке индекса.

Опция 2

  1. Оценка полнотекстового предиката
  2. Получение значений столбца DtPagina для всех соответствующихСтроки
  3. Сортируйте их и получите верхние 10.

При оценке первого варианта нижний план показывает, что он должен отсканировать около 600 строк, чтобы получить 10 соответствующихв состоянии выйти.Это огромная недооценка, так как на самом деле ни одна строка не соответствует предикату, и это необходимо сделать для целых 1 186 533 строк.

При оценке второго варианта из верхнего плана видно, что он предполагает наличие13,846.2 совпадающих строк, которые будут возвращены из запроса полнотекстового индекса и должны быть объединены и отсортированы.Это большая переоценка, поскольку фактическая цифра равна нулю.

Таким образом, эти неправильные оценки приводят к неправильному выбору первого варианта.

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

Редактировать: Это немного хак, но вполне может сработать.Что если вы попробуете

declare @term nvarchar(4000)= N'"DIEGO NUNES J COMBINADO"'
declare @num int = 10

SELECT TOP (@num) Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC

Тогда он примет TOP 100, что вполне может оказаться достаточным, чтобы склонить его к выбору другого более эффективного плана.

...