Вопрос о производительности 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 секунды.

спасибо очень много для всех, кто внес свой вклад в этот вопрос на этом сайте и на других.