Фильтр поиска SQL Server и порядок по проблемам производительности - PullRequest
2 голосов
/ 10 марта 2010

У нас есть функция табличного значения, которая возвращает список людей, к которым вы можете получить доступ, и у нас есть связь между поиском и человеком, называемым результатом поиска.

То, что мы хотим сделать, - это не выбирать всех людей из поиска и представлять их.

Запрос выглядит так

SELECT qm.PersonID, p.FullName 
FROM QueryMembership qm
INNER JOIN dbo.GetPersonAccess(1) ON GetPersonAccess.PersonID = qm.PersonID
INNER JOIN Person p ON p.PersonID = qm.PersonID
WHERE qm.QueryID = 1234

Есть только 25 строк с QueryID = 1234, но в таблице QueryMembership содержится почти 5 миллионов строк. В личном столе около 40 тысяч человек.

QueryID - это не PK, но это индекс. План запроса говорит мне, что 97% от общей стоимости затрачивается на «Поиск ключей» с предикатом поиска.

QueryMembershipID = Scalar Operator (QueryMembership.QueryMembershipID as QM.QueryMembershipID)

Почему PK там, когда он вообще не используется в запросе? и почему это занимает так много времени?

Количество человек всего 25, с индексом, это должно быть сканирование таблицы для всех строк QueryMembership с QueryID = 1234 и затем JOIN для 25 человек, которые существуют в функции табличных значений. Который, кстати, должен быть оценен только один раз и завершается менее чем за 1 секунду.

Ответы [ 5 ]

2 голосов
/ 14 марта 2010

, если вы хотите избежать «поиска ключа», используйте закрытый индекс

create index ix_QueryMembership_NameHere on QueryMembership (QueryID)
include (PersonID);

добавить больше имен столбцов, которые вы будете выбирать в include аргументах.

чтобы понять, почему «поиск ключа» в PK работает так медленно, попробуйте DBCC FREEPROCCACHE, ALTER INDEX ALL ON QueryMembership REBUILD, ALTER INDEX ALL ON QueryMembership REORGANIZE

Это может помочь, если индекс вашего ПК отключен, или кэш сохраняет неверный план.

2 голосов
/ 10 марта 2010

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

Но я должен сделать пару замечаний:

  • Вы уже пришли к выводу, что медлительность является результатом предложения ORDER BY. Я сомневаюсь. Реальный тест заключается в том, ускоряет ли удаление ORDER BY запрос, что вы еще не сделали. Доллары к пончикам, это не будет иметь никакого значения.

  • Вы можете получить "log n" в своем заявлении big-O, когда оптимизатор действительно выберет использование индекса, который вы определили. Это может не произойти, потому что ваш индекс может быть недостаточно избирательным. То, что делает ваше решение для временных таблиц быстрее, чем решение оптимизатора, состоит в том, что вы знаете кое-что о подмножестве возвращаемых данных, чего не знает оптимизатор (в частности, что это действительно небольшое подмножество данных). Если ваши индексы недостаточно избирательны для вашего запроса, оптимизатор не всегда может разумно предположить это, и он выберет план, который избегает того, что, по его мнению, могло бы стать наихудшим сценарием, состоящим из множества поисков индекса, а затем из множества поисков. а потом большой вид. Часто он предпочитает сканировать и хэшировать. Поэтому то, что вы сделали с временной таблицей, часто является способом решения этой проблемы. Часто вы можете сузить свои индексы или создать индексированное представление для подмножества данных, с которыми вы хотите работать. Все зависит от специфики вашей работы.

2 голосов
/ 10 марта 2010

Вы должны определить индексы для таблиц, которые вы запрашиваете. В частности, для столбцов, указанных в предложениях WHERE и ORDER BY.

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

0 голосов
/ 14 марта 2010

Вы пытались реструктурировать запрос в CTE для разделения вызова TVF? Итак, что-то вроде:

With QueryMembershipPerson
    (
    Select QM.PersonId, P.Fullname
    From QueryMembership As qm
        Join Person As P
            On P.PersonId = QM.PersonId
    Where QM.QueryId = 1234
    )
Select PersonId, Fullname
From QueryMembershipPerson As QMP
    Join dbo.GetPersonAccess(1) As PA
        On PA.PersonId = QMP.PersonId

РЕДАКТИРОВАТЬ : Кстати, я предполагаю, что есть индекс PersonId как в QueryMembership, так и в таблице Person.

РЕДАКТИРОВАТЬ Как насчет двух табличных выражений, таких как:

With 
    QueryMembershipPerson As
    (
    Select QM.PersonId, P.Fullname
    From QueryMembership As qm
        Join Person As P
            On P.PersonId = QM.PersonId
    Where QM.QueryId = 1234
    )
    , With PersonAccess As
    (
    Select PersonId
    From dbo.GetPersonAccess(1) 
    )
Select PersonId, Fullname
From QueryMembershipPerson As QMP
    Join PersonAccess As PA
        On PA.PersonId = QMP.PersonId

Еще одним решением будет производная таблица, например, так:

Select ...
From  (
        Select QM.PersonId, P.Fullname
        From QueryMembership As qm
            Join Person As P
                On P.PersonId = QM.PersonId
        Where QM.QueryId = 1234
        ) As QueryMembershipPerson
    Join dbo.GetPersonAccess(1)  As PA
        On PA.PersonId = QueryMembershipPerson.PersonId

Если перенести часть запроса во временную таблицу и затем присоединиться к ней, я буду удивлен, что вы не сможете объединить эту концепцию в CTE или запрос с производной таблицей.

0 голосов
/ 10 марта 2010

Вам нужны индексы в предложениях WHERE и ORDER BY. Я не эксперт, но могу поспорить, что он выполняет сканирование таблицы для каждой строки. Так как ваша проблема со скоростью решена путем удаления INNER JOIN или ORDER BY, я уверен, что проблема связана именно с объединением. Бьюсь об заклад, он делает сканирование таблицы на вашем объединенном столе из-за такого рода. Поместив сначала индекс в столбцы в вашем предложении WHERE, вы сможете увидеть, так ли это на самом деле.

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