Вопрос о производительности SQL Server HierarchyID depth-first
Я пытаюсь реализовать hierarchyID в таблице (dbo.[Сообщение]), содержащий примерно 50 000 строк (будет существенно расти в будущем). Однако для получения 25 результатов требуется 30-40 секунд.
корневой узел является наполнителем для обеспечения уникальности, поэтому каждая последующая строка является дочерним элементом этой фиктивной строки.
Мне нужно иметь возможность сначала пересечь глубину таблицы и сделать столбец hierarchyID (dbo.[Сообщение.]Код сообщения) кластеризации первичный ключ, также добавили вычисленный smallint (dbo.[Сообщение.]Hierarchy), который хранит уровень узла.
использование: приложение .Net проходит через значение hierarchyID в базу данных, и я хочу иметь возможность извлекать всех (если таковые имеются) детей и родителей этого узла (кроме корня, поскольку он является наполнителем).
упрощенная версия запроса, который я использую:
@MessageID hierarchyID /* passed in from application */
SELECT
m.MessageID, m.MessageComment
FROM
dbo.[Message] as m
WHERE
m.Messageid.IsDescendantOf(@MessageID.GetAncestor((@MessageID.GetLevel()-1))) = 1
ORDER BY
m.MessageID
из того, что я понимаю, индекс должен быть обнаружен автоматически без намек.
из поисковых форумов я видел людей, использующих индексные подсказки при работе с индексами ширины, но не наблюдал этого приложения в ситуациях глубины. Будет ли это уместным для моего сценария?
Я провел последние несколько дней, пытаясь найти решение этой проблемы, но безрезультатно. Я был бы очень признателен за любую помощь, и поскольку это мой первый пост, я заранее извиняюсь, если это будет считаться "noobish" вопрос, я прочитали документацию MS и искали бесчисленные форумы, но не наткнулись на краткое описание конкретной проблемы.
2 ответов
Не совсем ясно, пытаетесь ли вы оптимизировать поиск по глубине или по ширине; вопрос предполагает глубину, но комментарии в конце - о ширине.
у вас есть все индексы, необходимые для глубины (просто индексируйте ). Для широты-во-первых, недостаточно просто создать вычисленные level
столбец, вы также должны его индексировать:
ALTER TABLE Message
ADD [Level] AS MessageID.GetLevel()
CREATE INDEX IX_Message_BreadthFirst
ON Message (Level, MessageID)
INCLUDE (...)
(обратите внимание, что для некластеризованных индексов вы будете вероятно, нужно INCLUDE
- в противном случае SQL Server может прибегнуть к сканированию кластеризованного индекса.)
теперь, если вы пытаетесь найти все предков узла, вы хотите взять несколько другую тактику. Вы можете сделать эти поиски молниеносно, потому что-и вот что круто о hierarchyid
- каждый узел уже "содержит" всех своих предков.
Я использую функцию CLR, чтобы сделать это как можно быстрее, но вы можете сделать это с помощью рекурсивной CTE:
CREATE FUNCTION dbo.GetAncestors
(
@h hierarchyid
)
RETURNS TABLE
AS RETURN
WITH Hierarchy_CTE AS
(
SELECT @h AS id
UNION ALL
SELECT h.id.GetAncestor(1)
FROM Hierarchy_CTE h
WHERE h.id <> hierarchyid::GetRoot()
)
SELECT id FROM Hierarchy_CTE
теперь, чтобы получить всех предков и потомков, используйте его следующим образом:
DECLARE @MessageID hierarchyID /* passed in from application */
SELECT m.MessageID, m.MessageComment
FROM Message as m
WHERE m.MessageId.IsDescendantOf(@MessageID) = 1
OR m.MessageId IN (SELECT id FROM dbo.GetAncestors(@MessageID.GetAncestor(1)))
ORDER BY m.MessageID
попробуйте - это должно решить ваши проблемы с производительностью.
нашел решение здесь: http://connect.microsoft.com/SQLServer/feedback/details/532406/performance-issue-with-hierarchyid-fun-isdescendantof-in-where-clause#
просто напоминаю, что я начал с heirarchyID
передано из приложения, и моя цель-получить всех родственников этого значения (как предков, так и потомков).
в моем конкретном примере мне пришлось добавить следующие объявления перед SELECT
заявление:
declare @topNode hierarchyid = (select @messageID.GetAncestor((@messageID.GetLevel()-1)))
declare @topNodeParent hierarchyid = (select @topNode.GetAncestor(1))
declare @leftNode hierarchyid= (select @topNodeParent.GetDescendant (null, @topNode))
declare @rightNode hierarchyid= (select @topNodeParent.GetDescendant (@topNode, null))
на WHERE
предложение было изменено на:
messageid.IsDescendantOf(@topNode)=1 AND (messageid > @leftNode ) AND (messageid < @rightNode )
увеличение производительности запросов очень значительно:
для каждого результата прошло в, время поиска теперь 20ms в среднем (было от 120 до 420).
при запросе 25 значений, ранее потребовалось 25 - 35 секунд, чтобы вернуть все связанные узлы (в некоторых случаях каждое значение имело много родственников, в некоторых не было ни одного). Теперь это занимает всего 2 секунды.
спасибо очень много для всех, кто внес свой вклад в этот вопрос на этом сайте и на других.