来自同一个表的递归查询

时间:2017-01-16 09:58:07

标签: sql sql-server sql-server-2008-r2

我在一个表ProdHistory中有不同的产品序列号,其中包含表名称建议的生产历史记录。
例如,我有产品序列SER001,它使用具有自己序列号的部件 我们还生成这些部分,因此使用相同的表ProdHistory来跟踪其子部分 子部分也是如此,如果它有子部分。

样本表

IF OBJECT_ID('tempDB.dbo.#SAMPLETable') IS NOT NULL DROP TABLE #SAMPLETable
CREATE TABLE #SAMPLETable
(
    ITEMSEQ INT IDENTITY(1,1),
    SERIAL NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    ITEMID NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    PARTSERIAL NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    PARTID NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    CREATEDDATETIME DATETIME
)

INSERT INTO 
       #SAMPLETable (SERIAL,ITEMID,PARTSERIAL,PARTID,CREATEDDATETIME) 
VALUES ('SER0001','ASY-1342','ITM0001','PRT-0808','2017-01-17'),
       ('SER0001','ASY-1342','ITM0002','PRT-0809','2017-01-17'),
       ('SER0001','ASY-1342','ITM0003','PRT-0810','2017-01-17'),
       ('SER0001','ASY-1342','ITM0004','PRT-0811','2017-01-17'),
       ('ITM0001','PRT-0808','UNT0001','PRT-2020','2017-01-16'),
       ('ITM0002','PRT-0809','UNT0002','PRT-2021','2017-01-16'),
       ('ITM0002','PRT-0809','UNT0003','PRT-2022','2017-01-16'),
       ('ITM0003','PRT-0810','UNT0004','PRT-2023','2017-01-16'),
       ('UNT0002','PRT-2021','DTA0000','PRT-1919','2017-01-15'),
       ('UNT0003','PRT-2022','DTA0001','PRT-1818','2017-01-15'),
       ('DTA0001','PRT-1818','LST0001','PRT-1717','2017-01-14')

问题是,如果仅给出主序列号,我该如何返回与之关联的所有部分和子部分?

示例结果:

MainSerial SubSerial1 SubSerial2 SubSerial3 SubSerial4
-------------------------------------------------------
SER0001    ITM0001    UNT0001
SER0001    ITM0002    UNT0002    DTA0000
SER0001    ITM0002    UNT0003    DTA0001    LST0001
SER0001    ITM0003    UNT0004
SER0001    ITM0004   

在上文中,不确定序列号有多少部分和子部分 我没有发布我的代码,因为我现在正在做的是逐个查询 如果我知道子部分的数量,我可以做嵌套的Joins,但事实并非如此。

另一个问题是,如果我刚刚给出上述任何子部分,是否可以返回相同的结果?

1 个答案:

答案 0 :(得分:1)

我认为一种方法是使用动态SQL,如下所示:

-- Variables to generate SQL query string dynamically
declare @cols nvarchar(max) = '', @joins nvarchar(max) = '', @sql nvarchar(max) = '';    

-- Using CTE to iterate parent-child records
with cte(i, cols, joins, itemId, serial, partId, partSerial) as (
    select 
        1, -- Level or depth of hierarchically tree 
        N's1.serial MainSerial, s1.partSerial SubSerial'+cast(1 as varchar(max)), 
        N'yourTable s'+cast(1 as varchar(max)), 
        s.itemId, s.serial, s.partId, s.partSerial
    from yourTable s
    -- A way to filter root-parents is filtering items those are not in parts
    where s.itemId not in (select si.partId from yourTable si)
    union all
    select 
        i+1, 
        cols + N', s'+cast(i+1 as varchar(max))+N'.partSerial SubSerial'+cast(i+1 as varchar(max)), 
        joins + N' left join yourTable s'+cast(i+1 as varchar(max))+N' on s'+cast(i as varchar(max))+N'.partId = s'+cast(i+1 as varchar(max))+N'.itemId', 
        st.itemId, st.serial, st.partId, st.partSerial
    from cte 
    join #sampleTable st on cte.partId = st.itemId
)
-- Now we need only strings of deepest level
select top(1) 
    @cols = cols, @joins = joins
from cte
order by i desc;

-- Finalize and executing query string
set @sql = N'select ' + @cols + N' from ' + @joins + N' where s1.itemId not in (select s.partId from yourTable s)';
exec(@sql);

附加说明:生成的查询是:

select s1.serial MainSerial
    , s1.partSerial SubSerial1
    , s2.partSerial SubSerial2
    , s3.partSerial SubSerial3
    , s4.partSerial SubSerial4 
  --, ...
from yourTable s1 
  left join yourTable s2 on s1.partId = s2.itemId 
  left join yourTable s3 on s2.partId = s3.itemId 
  left join yourTable s4 on s3.partId = s4.itemId 
--left join ...
where s1.itemId not in (select s.partId from yourTable s);