Вопрос о производительности SQL Server HierarchyID в глубину - PullRequest
8 голосов
/ 26 апреля 2010

Я пытаюсь внедрить иерархический идентификатор в таблицу (dbo. [Message]), содержащую примерно 50 000 строк (значительно увеличится в будущем). Однако для получения примерно 25 результатов требуется 30-40 секунд.

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

Мне нужно иметь возможность сначала пройти по глубине таблицы и сделать столбецierarchyID (dbo. [Message] .MessageID) первичным ключом кластеризации, а также добавить вычисленный smallint (dbo. [Message] .Hierarchy). который хранит уровень узла.

Использование. Приложение .Net проходит через значение иерархии в базу данных, и я хочу иметь возможность извлекать всех (если они есть) дочерних элементов И родителей этого узла (кроме корневого, поскольку он является заполнителем).

Упрощенная версия запроса, который я использую:

@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

Из того, что я понимаю, индекс должен определяться автоматически без подсказки.

Из поисковых форумов я видел людей, использующих подсказки по индексам при работе с индексами первой ширины, но не наблюдал это приложение в ситуациях с глубиной. Будет ли это подходящим подходом для моего сценария?

Последние несколько дней я пытался найти решение этой проблемы, но безрезультатно. Я был бы очень признателен за любую помощь, и, поскольку это мой первый пост, я заранее прошу прощения, если это будет считаться «нубистским» вопросом, я прочитал документацию по MS и искал бесчисленные форумы, но не нашел краткого описания конкретная проблема.

Ответы [ 2 ]

8 голосов
/ 26 апреля 2010

Не совсем ясно, пытаетесь ли вы оптимизировать поиск по глубине или по ширине; вопрос предполагает глубину, но комментарии в конце о ширине.

У вас есть все индексы, которые вам нужны для глубины (просто индексируйте столбец hierarchyid). Во-первых, недостаточно просто создать вычисляемый столбец 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

Попробуйте - это должно решить ваши проблемы с производительностью.

2 голосов
/ 27 апреля 2010

Найден обходной путь здесь: 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 )

Увеличение производительности запросов очень значительно:

Для каждого переданного результата время поиска теперь составляет в среднем 20 мс (было от 120 до 420).

При запросе 25 значений ранее требовалось 25–35 секунд, чтобы вернуть все связанные узлы (в некоторых случаях у каждого значения было много родственников, в некоторых их не было). Теперь это занимает всего 2 секунды.

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

...