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

У меня есть коммерческое приложение с базой данных SQL Server 2008 R2, которая содержит таблицу, которая должна моделировать структуру файлов и папок, аналогичную ‹root>:‹sublevel 1>:‹sublevel 2>..‹sublevel n>:‹ документ>. Таблица базы данных содержит пары значений (элемент_папки, родитель), где элемент-папка — это либо документ, либо папка, а родитель — ссылка на другую строку в таблице.

Я пытаюсь написать запрос, который возвращает два значения в строке:

  • документ
  • Полный сцепленный путь к документу от папки верхнего уровня до непосредственного контейнера документа, например Root:Level1:Level2..:LevelN.

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

Вместо (документ, родитель) (родитель, дедушка и бабушка) (прародитель, прадедушка) ... (предок, корень)

Таблица содержит (документ, родитель) (документ, прародитель) ... (документ, предок) (документ, корень) (родитель, прародитель) ... (родитель, предок) (родитель, корень) ... (прародитель, прапрадедушка) ... (прародитель, корень)

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

Кто-нибудь еще сталкивался с подобной проблемой, и можно ли ее решить, не прибегая к сложному программированию, выходящему за рамки стандартных возможностей SQL?

Спасибо большое

Патрик


person Patrick McManus    schedule 20.05.2016    source источник
comment
Как насчет того, чтобы опубликовать некоторые структуры таблиц и образцы данных? Если вы просто основываете свою рекурсию на имени, вы, вероятно, проигрываете битву.   -  person Sean Lange    schedule 20.05.2016
comment
Рекурсия основана на уникальном, но не PK VARCHAR значении DOCNUMBER, где столбец PARENT содержит DOCNUMBER строки-предка.   -  person Patrick McManus    schedule 26.05.2016
comment
К счастью, оказалось, что было принято решение игнорировать существующую иерархию папок, и проблема исчезла благодаря (мудрому) бизнес-решению. Спасибо Шону и Табу за ответы.   -  person Patrick McManus    schedule 26.05.2016


Ответы (1)


Если у вас есть:

(документ, родитель) (документ, прародитель) ... (документ, предок) (документ, корень) (родитель, прародитель) ... (родитель, предок) (родитель, корень) ... (прародитель, прапрародитель ) ... (прародитель, корень)

Вы хотите ограничить свою рекурсию только:

(документ, родитель)(родитель, дедушка и бабушка)(прародитель, прадедушка) (прадедушка, корень)

Правильно? Таким образом, хитрость заключается в том, чтобы просто выяснить, какие условия поставить в нижнюю половину вашего рекурсивного cte, чтобы не ВСЕ родственники текущей строки присоединялись к текущей строке.

Итак, для document вам нужно parent. Для parent вам нужно grandparent и так далее. Как мы можем создать этот фильтр?

Итак, для document что верно в отношении parent, чего нет в отношении любого другого предка?

Ответ: document имеет всех тех же родственников, что и parent, но только document имеет предком parent.

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

Представьте себе интервью: «Итак, вы один из моих предков. Если ни у кого из других моих предков вы также не являетесь предком, то вы должны быть моим непосредственным родителем».

Правда, нет?

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

{for recursion -- Join second half of UNION back to main CTE}
ON bottom.Folder_Item=cte.parent  --you are my ancestor
AND NOT EXISTS(
 SELECT * FROM MyTable mt --others
 WHERE mt.folder_item<>bottom.folder_item --the other is not you
 AND mt.Folder_Item=cte.parent --the other is also my ancestor
 AND mt.Parent=bottom.folder_item --the other also has you as ancestor
)

Для этого фрагмента bottom — это мой псевдоним для нижней половины вашего рекурсивного UNION, а cte — это псевдоним всего CTE.

И, поместив этот фильтр в условие JOIN для рекурсии, я думаю, это должно предотвратить схождение дерева рекурсии с ума и превышение предела рекурсии. Я думаю!

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

Я думаю, что это должно работать, но если нет, вы определенно можете делать то, что вам нужно, с помощью КУРСОРА.

person Tab Alleman    schedule 20.05.2016