Условно-рекурсивный запрос в SQL Server

У меня есть эти 2 таблицы:

ТАБЛИЦА 1

Идентификатор Короткое имя Родительский идентификатор С1 Ребенок1 П1 П1 Родитель1 ГП1 ГП1 ДедушкаРодитель1 НУЛЕВОЙ С2 Ребенок2 П2 П2 Родитель2 ГП1

ТАБЛИЦА 2

Идентификатор Параметр ГП1 5 С2 1

Логика следующая: если мы не находим значение параметра в таблице 2, мы поднимаемся вверх по иерархии, определенной в таблице 1, пока что-нибудь не найдем.

В примере:

  • если мне нужна настройка для элемента C2, я получу 1 (прямое значение C2)

  • если мне нужна настройка для элемента C1, я получу 5 (у меня ничего нет в таблице 2, я посмотрю на ее родительский элемент -P1 относительно таблицы 1-, все равно ничего, я посмотрю на его родительский элемент -GP1-)

  • если мне нужна настройка для элемента P2, я получу 5 (родительский элемент — GP1)

Я пытался реализовать рекурсивный запрос, но пока безуспешно, мне нужна помощь.

Спасибо

На данный момент лучшее, что у меня есть:

with settings as
(
    select N.Id,N.ParentId, N.ShortName,DTC.Setting 
    from Table1 as N 
    left join Table2 as DTC on DTC.Id=N.id
)
, RecursiveTable AS (
    SELECT no.Id,ShortName,ParentId,Setting 
    FROM settings as no
    UNION ALL
    SELECT n.Id,n.ShortName, n.ParentId,n.Setting 
    FROM settings n

    INNER JOIN RecursiveTable rn ON n.id = rn.ParentId
)
select * from RecursiveTable

какой запрос вы пробовали?

Daniel A. White 13.06.2024 16:44

Эта скрипта может помочь нам совместно найти решение dbfiddle.uk/eCLSpSXh

Bart McEndree 13.06.2024 16:46

Я не вижу никаких проблем в рекурсивном запросе, который вы пробовали, интересно, почему это так?

GuidoG 13.06.2024 16:59

@GuidoG, я думаю, проблема в том, что даже если у вас есть родители и т. д., получить настройку сложно, поскольку она может исходить от разных родительских уровней

siggemannen 13.06.2024 17:12

@siggemannen Я написал этот комментарий как саркастический, поскольку, когда я его писал, запрос еще не был виден для нас

GuidoG 14.06.2024 07:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
76
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Не рекурсивно, но дает ожидаемые результаты

SELECT Id, Setting, 'From Table 2' as Source
FROM TABLE2   

UNION ALL  

SELECT T1.Id,t2.Setting, 'Direct child via Table 1' as Source
FROM TABLE2 t2
INNER JOIN TABLE1 t1 ON t2.Id=t1.ParentId
WHERE T2.ID <> T1.ID

UNION ALL  

SELECT T1.Id,t2.Setting, 'Grand child via Table 1 2x' as Source
FROM TABLE2 t2
INNER JOIN TABLE1 t3 ON t2.Id=t3.ParentId
INNER JOIN TABLE1 t1 ON t3.Id=t1.ParentId
WHERE  t1.ID not in (Select ID FROM table2)

рабочий пример

Идентификатор Параметр Источник ГП1 5 Из таблицы 2 С2 1 Из таблицы 2 П1 5 Прямой дочерний элемент через Таблицу 1 П2 5 Прямой дочерний элемент через Таблицу 1 С1 5 Внук по Таблице 1 2x

Это не сработает, поскольку мы не знаем уровень иерархии, может быть 1 как 10. Спасибо.

Manta 14.06.2024 10:24
Ответ принят как подходящий

Я добавил некоторые настройки в ваш пример, возможно, вы можете сделать что-то вроде этого:

;WITH Table1 AS(
SELECT  *
FROM    (
    VALUES  (N'C1', N'Child1', N'P1')
    ,   (N'P1', N'Parent1', N'GP1')
    ,   (N'GP1', N'GrandParent1', NULL)
    ,   (N'C2', N'Child2', N'P2')
    ,   (N'P2', N'Parent2', N'GP1')
) t (Id,Shortname,ParentId)
)
, table2 AS (
SELECT  *
FROM    (
    VALUES  (N'GP1', 5)
    ,   (N'C2', 1)
    ,   (N'C1', 10)
) t (Id,Setting)
)
, hierarchy AS (
    SELECT  id, shortname, parentid, 1 AS level
    FROM    table1 t
    
    UNION ALL
    SELECT  h.id, h.shortname, t.parentid, h.level + 1
    FROM    hierarchy h
    INNER JOIN table1 t
        ON  t.ID = h.ParentID
    )
, settings AS (
    SELECT  h.ID, h.shortname, setting.ID AS SettingID, setting.Setting
    ,   ROW_NUMBER() OVER(PARTITION BY h.ID ORDER BY h.level, case when setting.Id = h.id then 0 else 1 end) AS sort
    FROM    hierarchy h
    INNER JOIN table2 setting
        ON  setting.Id IN (h.id, h.parentid)
    )
SELECT  *
FROM    settings s
WHERE   s.sort = 1
  1. Я создаю hierarchy-CTE идентификаторов, беря все строки и затем переходя к их родительскому и родительскому идентификаторам. Столбец уровня отслеживает, где мы находимся в дереве.
  2. Затем внутри settings-CTE мы присоединяемся к «таблице» настроек по значению идентификатора или родительского идентификатора, это позволяет нам получить все значения настроек в дереве.
  3. ROW_NUMBER() OVER(PARTITION BY h.ID ORDER BY h.level, case when setting.Id = h.id then 0 else 1 end) создает рейтинг для настройки, поскольку нам нужен ближайший уровень, мы упорядочиваем его по h.level, а затем по тому, насколько он соответствует h.Id или нет.
  4. Наконец, мы извлекаем все настройки, принадлежащие самому нижнему уровню, выполнив: WHERE s.sort = 1

Выход:

ИДЕНТИФИКАТОР короткое имя Идентификатор настройки Параметр Сортировать С1 Ребенок1 С1 10 1 С2 Ребенок2 С2 1 1 ГП1 ДедушкаРодитель1 ГП1 5 1 П1 Родитель1 ГП1 5 1 П2 Родитель2 ГП1 5 1

Я думаю, что у вас было концептуально обратное рекурсивное соединение, поскольку оно относится к родителю и дочернему элементу. Возможно, есть что-то более элегантное, но это соответствует пути, по которому вы шли:

with Base as (
    select t.Id, t.ParentId, t.ShortName, DTC.Setting 
    from Table1 as t left outer join Table2 as DTC on DTC.Id = t.id
), Settings as (
    select Id, ShortName, Setting,
        case when setting is not null then Id end as RootId,
        case when setting is null then ParentId end as ParentId
    from Base
    union all
    select s.Id, s.ShortName, b.Setting,
        case when b.setting is not null then b.Id end,
        case when b.setting is null then b.ParentId end
    from Settings s inner join Base b on b.Id = s.ParentId
)
select Id, ShortName, Setting, RootId from Settings
where Setting is not null;

https://dbfiddle.uk/SIkgyCsO

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

Другие вопросы по теме