#sql #sql-server-2008 #recursion
#sql #sql-server-2008 #рекурсия
Вопрос:
Я пытаюсь выполнить рекурсивный запрос, но у якоря и члена cte есть объединение. Возможно ли это? Ошибка, которую я получаю, это
Recursive member of a common table expression 'mainMenu' has multiple recursive references.
Цель объединения — сделать пункт меню видимым для роли или отдельного пользователя. Если есть лучший способ сделать это, я весь внимание. TIA!
with
mainMenu (...)
as (
select ...
from (
-- role
select ...
from ....
and parentID is null
UNION
--user
select ...
from ....
and parentID is null
)as cteAnchor
UNION ALL
select ...
from (
-- role
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
UNION
-- user
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
)as cteChildren
)
select
...
from
mainMenu
Ответ №1:
Вы не можете ссылаться на mainMenu
более одного раза. И это вызвано тем фактом, что у вас фактически есть два выражения привязки, одно для ролей и одно для пользователей. Есть два способа исправить это. Вы могли бы разделить свой запрос на два CTE (один для ролей, другой для пользователей). Вот так:
with
roleMainMenu (...)
as
(
-- role
select ...
from ....
and parentID is null
union all
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
),
userMainMenu (...)
as
(
-- user
select ...
from ....
and parentID is null
union all
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
)
select * from roleMainMenu
union
select * from userMainMenu
Или вы могли бы заранее объединить выражения привязки роли и пользователя. Я не знаю, является ли запрос на получение дочерних элементов общим как для элементов меню роли, так и для пользователя, в противном случае вы могли бы использовать выражение привязки, которое имеет объединение для корневых элементов роли и пользователя.
with mainMenu (...)
as
(
select root.* from
(
-- role roots
select .. from ... and parentID is null
union
-- user roots
select .. from ... and parentID is null
) root
union all
select ... from root
inner join mainMenu on parentID = mainMenu.id
)
select *
from mainMenu
Комментарии:
1. Отлично, спасибо. Похоже, это в основном то, что мне нужно. Единственная проблема сейчас в том, что если у якоря есть роль, но у дочернего элемента есть пользователь, я не получаю пользователя из-за внутреннего соединения. Хотя я думаю, что это моя проблема. Это ответило на мой первоначальный вопрос, и я благодарен!
Ответ №2:
Проблема, по-видимому, заключается в синтаксисе, который вы используете со своим объединением all. Объединение all отличается от объединения тем, что оно объединяет все строки, выбранные вами из одной таблицы, и все строки, выбранные вами из другой таблицы, в одну большую таблицу. Когда вы выполняете объединение all, столбцы, которые вы выбираете из разных таблиц, должны выстраиваться в линию, чтобы sql знал, как поместить все данные в одну таблицу.
И наоборот, существует объединение. В отличие от объединения all, которое добавляет информацию в виде дополнительных данных, объединение добавляет больше информации в ту же строку. Объединение позволяет нам объединить информацию и узнать больше об отдельной точке данных.
Я привожу оба этих ключевых слова, потому что вы, по сути, объединили синтаксис из union all и join . Вашему внутреннему запросу требуется выбрать, чтобы перейти ко второму from, и псевдонимы таблиц должны быть удалены. Ваш внутренний запрос должен выглядеть примерно так:
select ...
from (
-- role
select ...
from ....
and parentID is null
UNION
--user
select ...
from ....
and parentID is null
)
UNION ALL
select ...
from (
-- role
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
UNION
-- user
select ...
from ....
inner join mainMenu on parentID = mainMenu.id
)
Комментарии:
1. Спасибо, Дженн, у меня есть select в моем фактическом коде, но не удалось перенести его в псевдокод, я обновил OP, чтобы отразить. Что касается объединения all … вот как выполняется рекурсивный запрос cte, если я чего-то не упускаю? technet.microsoft.com/en-us/library/ms186243 (v=sql.105).aspx
2. @Travis, для меня объединение выглядит правильным. Вы пробовали удалять объединение внутри второго подзапроса? Я думаю, проблема может заключаться в том, что вы дважды ссылаетесь на MainMenu.