Рекурсивный запрос, в котором привязка и элемент имеют объединения

#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.