SQL присоединиться с помощью рекурсивного CTE - PullRequest
0 голосов
/ 22 мая 2018

Редактировать: добавлен еще один сценарий наблюдения в примечаниях и обновлен образец вложения.

Я пытаюсь написать sql, чтобы получить вывод вместе с этим вопросом вместе с образцами данных.Есть две таблицы, одна с разными идентификаторами (pk) с их текущим флагом.другой с активным идентификатором (fk для pk из первой таблицы) и неактивным идентификатором (fk для pk из первой таблицы) Окончательный результат должен возвращать два столбца, первый столбец состоит из всех отличных идентификаторов из первой таблицы, а второй столбец должен содержатьАктивный идентификатор из 2-й таблицы.Ниже приведен sql:

IF OBJECT_ID('tempdb..#main') IS NOT NULL DROP TABLE #main;
IF OBJECT_ID('tempdb..#merges') IS NOT NULL DROP TABLE #merges
IF OBJECT_ID('tempdb..#final') IS NOT NULL DROP TABLE #final

SELECT DISTINCT id, 
       current
INTO   #main 
FROM   tb_ID t1 


--get list of all active_id and inactive_id 

SELECT DISTINCT active_id, 
            inactive_id, 
            Update_dt  
INTO   #merges 
FROM   tb_merges 
-- Combine where the id from the main table matched to the inactive_id (should return all the rows from #main)

   SELECT id, 
   active_id AS merged_to_id 
   INTO   #final 
   FROM   (SELECT t1.*, 
           t2.active_id, 
           Update_dt , 
           Row_number() 
             OVER ( 
               partition BY id, active_id 
               ORDER BY Update_dt DESC) AS rn 
    FROM   #main t1 
           LEFT JOIN #merges t2 
                  ON t1.id = t2.inactive_id) t3 
  WHERE  rn = 1 

 SELECT *
 FROM #final

Этот sql частично работает.Это не работает, когда идентификатор когда-то был активным, а затем становится неактивным.Обратите внимание:

  • активный идентификатор должен возвращать последний самый активный идентификатор
  • идентификатор, у которого нет активного идентификатора, должен быть либо нулевым, либо сам идентификатор
  • ID, где current = 0, в этих случаях активный ID должен быть идентификатором current в tb_ID

  • ID могут взаимозаменяться.Например, есть два идентификатора 6 и 7, когда 6 активен, 7 неактивен, и наоборот.единственный способ узнать наиболее актуальное активное состояние - по дате обновления

Прикрепленный образец может быть легко понятен

Sample data with expected output

Похоже, мне придется использовать рекурсивный метод cte для достижения результатов.Может кто-нибудь, пожалуйста, помогите?Спасибо за ваше время!

1 Ответ

0 голосов
/ 22 мая 2018

Я думаю, вы правы, что рекурсивный CTE выглядит хорошим решением для этого.Я не полностью уверен, что я точно понял, о чем вы просите, особенно в отношении столбца update_dt, просто потому, что данные немного абстрактны как есть, но я 'Мы попробовали это сделать, и, похоже, он работает с вашими образцами данных.Комментарии объясняют, что происходит.

declare @tb_id table (id bigint, [current] bit);
declare @tb_merges table (active_id bigint, inactive_id bigint, update_dt datetime2);
insert @tb_id values
    -- Sample data from the question.
    (1, 1),
    (2, 1),
    (3, 1),
    (4, 1),
    (5, 0),
    -- A few additional data to illustrate a deeper search.
    (6, 1),
    (7, 1),
    (8, 1),
    (9, 1),
    (10, 1);

insert @tb_merges values
    -- Sample data from the question.
    (3, 1, '2017-01-11T13:09:00'),
    (1, 2, '2017-01-11T13:07:00'),
    (5, 4, '2013-12-31T14:37:00'),
    (4, 5, '2013-01-18T15:43:00'),
    -- A few additional data to illustrate a deeper search.
    (6, 7, getdate()),
    (7, 8, getdate()),
    (8, 9, getdate()),
    (9, 10, getdate());

if object_id('tempdb..#ValidMerge') is not null
    drop table #ValidMerge;

-- Get the subset of merge records whose active_id identifies a "current" id and
-- rank by date so we can consider only the latest merge record for each active_id.
with ValidMergeCTE as
(
    select
        M.active_id,
        M.inactive_id,
        [Priority] = row_number() over (partition by M.active_id order by M.update_dt desc)
    from 
        @tb_merges M
        inner join @tb_id I on M.active_id = I.id 
    where
        I.[current] = 1
)
select
    active_id,
    inactive_id
into
    #ValidMerge
from
    ValidMergeCTE
where
    [Priority] = 1;

 -- Here's the recursive CTE, which draws on the subset of merges identified above.
 with SearchCTE as
 (
    -- Base case: any record whose active_id is not used as an inactive_id is an endpoint.
    select
        M.active_id,
        M.inactive_id,
        Depth = 0
    from
        #ValidMerge M
    where
        not exists (select 1 from #ValidMerge M2 where M.active_id = M2.inactive_id)

    -- Recursive case: look for records whose active_id matches the inactive_id of a previously
    --                 identified record.
    union all
    select
        S.active_id,
        M.inactive_id,
        Depth = S.Depth + 1
    from
        #ValidMerge M
        inner join SearchCTE S on M.active_id = S.inactive_id
 )
 select
    I.id,
    S.active_id
 from
    @tb_id I
    left join SearchCTE S on I.id = S.inactive_id;

Результаты:

id      active_id
------------------
1       3
2       3
3       NULL
4       NULL
5       4
6       NULL
7       6
8       6
9       6
10      6
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...