FreeText Query медленный - включает в себя TOP и Order By - PullRequest
4 голосов
/ 30 апреля 2010

Таблица Product содержит 700K записей. Запрос:

SELECT TOP 1 ID, Name FROM Product WHERE contains(Name, '"White Dress"') ORDER BY DateMadeNew desc

занимает около 1 минуты для запуска. Существует некластеризованный индекс для DateMadeNew и индекс FreeText для Name.

Если я удаляю TOP 1 или Order By - для запуска потребуется менее 1 секунды.

Вот ссылка на план выполнения. http://screencast.com/t/ZDczMzg5N

Похоже, что FullTextMatch имеет более 400K выполнений. Почему это происходит? Как это можно сделать быстрее?

ОБНОВЛЕНИЕ 5/3 / 2010

Похоже, кардинальность не в порядке при поиске в нескольких словах в свободном тексте:

По оценкам Оптимизатора, 28К записей соответствуют «Белому платью», в то время как на самом деле их всего 1. http://screencast.com/t/NjM3ZjE4NjAt

Если я заменю «Белое платье» на «Белое», предполагаемое число будет «27, 951», а фактическое число «28, 487», что намного лучше.

Похоже, что Оптимизатор использует только первое слово в фразе для поиска кардинальности.

Ответы [ 6 ]

1 голос
/ 25 сентября 2012

У меня раньше была такая же проблема.

Производительность зависит от того, какой уникальный индекс вы выбрали для полнотекстовой индексации. В моей таблице есть два уникальных столбца - ID и article_number.

Запрос:

select top 50 id, article_number, name, ... 
from ARTICLE 
CONTAINS(*,'"BLACK*" AND "WHITE*"')
ORDER BY ARTICLE_NUMBER

Если полнотекстовый индекс связан с ID, то он медленный в зависимости от искомых слов. Если полнотекстовый индекс связан с индексом ARTICLE_NUMBER UNIQUE, то он всегда был быстрым.

1 голос
/ 30 декабря 2010

У меня есть лучшее решение.

I. Давайте сначала рассмотрим предложенные решения, так как они также могут быть использованы в некоторых случаях:

  1. OPTION (HASH JOIN) - не подходит, так как вы можете получить ошибку "Обработчику запросов не удалось создать план запроса из-за подсказок, определенных в этом запросе. Повторите запрос без указания каких-либо подсказок и без используя SET FORCEPLAN. "

  2. ВЫБРАТЬ ТОП 1 * ОТ (ORIGINAL_SELECT) ORDER BY ... - не хорошо, когда вам нужно использовать результаты разбивки на страницы с разбивкой по страницам ORIGINAL_SELECT

  3. sp_create_plan_guide - это не хорошо, поскольку для использования plan_guide необходимо сохранить план для конкретного оператора SQL, это не будет работать для динамических операторов SQL (например, сгенерированных ORM)

II. Мое решение состоит из двух частей 1. Самостоятельная таблица соединений, используемая для полнотекстового поиска 2. Используйте MS SQL HASH Join Hints MSDN Join Hints

Ваш SQL:

SELECT TOP 1 ID, Name FROM Product WHERE contains(Name, '"White Dress"') 
ORDER BY DateMadeNew desc

Должен быть переписан как:

SELECT TOP 1 p.ID, p.Name FROM Product p INNER HASH JOIN Product fts ON fts.ID = p.ID
WHERE contains(fts.Name, '"White Dress"') 
ORDER BY p.DateMadeNew desc

Если вы используете NHibernate с / без Castle Active Records, я ответил в post , как написать перехватчик для изменения вашего запроса, чтобы заменить INNER JOIN на INNER HASH JOIN

1 голос
/ 01 мая 2010

Редактировать

С http://technet.microsoft.com/en-us/library/cc721269.aspx#_Toc202506240

Самое главное, что правильный тип соединения выбран для полнотекстовый запрос. мощность оценка на FulltextMatch STVF очень важно для правильного плана. Итак, первое, что нужно проверить, это FulltextMatch оценка мощности. Это приблизительное количество хитов в указателе для полнотекстового поиска строка. Например, в запросе в Рисунок 3 это должно быть близко к количество документов, содержащих термин «слово». В большинстве случаев это должно быть очень точным, но если оценка был далеко, вы могли генерировать плохие планы. Оценка для отдельные термины, как правило, очень хорошо, но оценивая несколько терминов, таких как фразы или AND-запросы более сложны поскольку невозможно узнать что пересечение терминов в индексе будет основываться на частоте условия в индексе. Если мощность оценка хорошая, плохой план вероятно вызвано запросом оптимизатор стоимости модели. Единственный способ исправить проблему плана - использовать запрос намек на принудительное соединение или оптимизировать для.

Таким образом, по информации, хранящейся в нем, он просто не может знать, могут ли два поисковых термина быть вместе достаточно независимыми или обычно встречаться вместе. Возможно, вам нужно иметь 2 отдельные процедуры: одну для запросов с одним словом, для которой вы позволяете оптимизатору выполнять свою работу, и одну для процедур из нескольких слов, для которых вы навязываете «достаточно хороший» план (sys.dm_fts_index_keywords может помочь, если вы не хотите один размер подходит для всего плана).

Примечание: вашей процедуре с одним словом, скорее всего, понадобится опция WITH RECOMPILE, рассматривающая этот фрагмент статьи.

В полнотекстовом поиске SQL Server 2008 у нас есть возможность изменить план, сгенерированный на основе оценки количества используемых поисковых терминов. Если план запроса фиксирован (как в параметризованном запросе внутри хранимой процедуры), этот шаг не выполняется. Поэтому скомпилированный план всегда обслуживает этот запрос, даже если этот план не идеален для данного поискового запроса.

Оригинальный ответ

Ваш новый план все еще выглядит довольно плохо. Похоже, что он возвращает только 1 строку из полнотекстовой части запроса, но сканирует все 770159 строк в таблице Product.

Как это работает?

CREATE TABLE #tempResults
(
ID int primary key,
Name varchar(200),
DateMadeNew datetime
)

INSERT INTO #tempResults
SELECT 
      ID, Name, DateMadeNew 
      FROM Product 
      WHERE contains(Name, '"White Dress"')


SELECT TOP 1
    *
    FROM #tempResults
    ORDER BY DateMadeNew desc
1 голос
/ 30 апреля 2010

Похоже, что FullTextMatch имеет более 400K выполнений. Почему это происходит?

Поскольку у вас есть индекс, объединенный с TOP 1, оптимизатор считает, что будет лучше пройти по индексу, проверяя каждую запись на наличие записи.

Как это можно сделать быстрее?

Если обновление статистики не помогает, попробуйте добавить подсказку к вашему запросу:

SELECT  TOP 1 *
FROM    product pt
WHERE   CONTAINS(name, '"test1"')
ORDER BY
        datemadenew DESC
OPTION (HASH JOIN)

Это заставит движок использовать алгоритм HASH JOIN для объединения вашей таблицы и вывода полнотекстового запроса.

Полнотекстовый запрос рассматривается как удаленный источник, возвращающий набор значений, проиндексированных KEY INDEX, предоставленных в определении FULLTEXT INDEX.

Обновление:

Если ваш ORM использует параметризованные запросы, вы можете создать руководство плана.

  • Используйте Profiler для перехвата запроса, который ORM посылает дословно
  • Сгенерируйте правильный план в SSMS, используя подсказки, и сохраните его как XML
  • Используйте sp_create_plan_guide с OPTION USE PLAN, чтобы оптимизатор всегда использовал этот план.
1 голос
/ 30 апреля 2010

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

, если он работает быстро без TOP и ORDER BY, попробуйте сделать это:

SELECT TOP 1
    *
    FROM (SELECT 
              ID, Name, DateMadeNew 
              FROM Product 
              WHERE contains(Name, '"White Dress"')
         ) dt
    ORDER BY DateMadeNew desc
0 голосов
/ 01 мая 2010

Несколько мыслей по этому поводу:

1) Обновили ли вы статистику в таблице товаров? Также было бы полезно увидеть оценки и фактическое количество строк в операциях.

2) Какую версию SQL Server вы используете? У меня была похожая проблема с SQL Server 2008, которая оказалась не чем иным, как без установленного пакета обновления 1. Установите SP1 и запрос FreeText, который занимал пару минут (из-за огромного количества фактических выполнений по сравнению с фактическим), занял секунду.

...