使用递归cte的sql连接

使用递归cte的sql连接,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,编辑:在注释中添加了另一个案例场景,并更新了示例附件 我正在尝试编写一个sql,以获得与此问题一起附带的输出以及示例数据。 有两个表,一个表具有不同的ID(pk)和当前标志。 另一个具有活动ID(fk到第一个表中的pk)和非活动ID(fk到第一个表中的pk) 最终输出应返回两列,第一列包含第一个表中的所有不同ID,第二列应包含第二个表中的活动ID。 以下是sql: IF OBJECT_ID('tempdb..#main') IS NOT NULL DROP TABLE #main; IF OBJ

编辑:在注释中添加了另一个案例场景,并更新了示例附件

我正在尝试编写一个sql,以获得与此问题一起附带的输出以及示例数据。 有两个表,一个表具有不同的ID(pk)和当前标志。 另一个具有活动ID(fk到第一个表中的pk)和非活动ID(fk到第一个表中的pk) 最终输出应返回两列,第一列包含第一个表中的所有不同ID,第二列应包含第二个表中的活动ID。 以下是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一度处于活动状态,然后变为非活动状态。 请注意:

  • 活动ID应返回最后一个最活跃的ID
  • 没有任何活动ID的ID应为null或ID本身
  • 当前ID=0时,在这些情况下,活动ID应为tb_ID中的当前ID

  • 身份证可以互换。例如,有两个ID 6和7,当6处于活动状态时,7处于非活动状态,反之亦然。了解最新活动状态的唯一方法是通过更新日期

随附的样本可能很容易理解

看起来我可能不得不使用递归cte来获得结果。有人能帮忙吗?
谢谢你抽出时间

我认为您是对的,递归CTE看起来是一个很好的解决方案。我不能完全确定我是否完全理解了您的要求,特别是关于
update\u 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

这是MySQL还是SQL Server?@Shawn-SQL Server您的初始数据是什么样子的?您需要解释“此SQL部分工作。它不工作,id曾经处于活动状态,然后变为非活动状态。”因为这毫无意义。您需要提供ddl和样本数据以及所需的输出。这是一个很好的起点@Shawn已经用实际输出和预期输出更新了附件。请让我知道它是否有意义。谢谢你在这方面的工作。我尝试在我的表中使用sql,查询从15分钟开始运行。看起来像是性能问题。@executive\u to\u learn-我在原始答案的评论中提到,对于我所说的“有效合并”,您可以尝试使用临时表而不是CTE,但我应该更清楚。我已经对查询进行了重构,I/O统计数据看起来好多了。请尝试一下新版本。嗨,Joe,在我的表上处理您的查询时,我发现当ID被交换时,还有一个逻辑缺失。例如,让我们在表中再添加两个id,并将它们命名为ID11和ID12。在合并表中,当ID 11处于活动状态时,ID 12处于非活动状态,反之亦然。查询对这两个值都返回null,而对于ID 11,它应该返回12,对于ID 12,它应该返回null。请再次运行您的查询,我尝试编辑它并查看结果以更好地理解它。@Excited\u to\u learn-在这种情况下,我的查询将为11和12返回null,因为您建议的合并记录都不满足递归CTE的基本情况。这是一件好事,他们没有,因为如果他们中的任何一个做了,递归将永远不会终止;它将在11和12之间来回反弹,直到达到
MAXRECURSION
级别。@executive\u to\u learn-直到现在为止,您已经很好地描述了应该管理此查询的规则,但您还没有真正解释这些数据的含义,我想这就是让我无法了解你的最新情况的原因。