SQL Server 2012:生成列值动态地基于其他列值的连接

时间:2017-09-12 22:58:38

标签: sql sql-server tsql sql-server-2012

我需要根据表中的可用备份产品生成通用备份代码。以下是备份产品表

if object_id('tempdb..#pdt') is not null
DROP TABLE #pdt
create table #pdt(ROW_NUM INT IDENTITY(1,1),  PRODUCT NVARCHAR(30), PDT_CODE 
NVARCHAR(10),BKP_PRODUCT   NVARCHAR(30),BKP_PDT_CODE NVARCHAR(10))

INSERT #pdt( PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
VALUES ('HP','HP','DELL SERIES','DS'),('HP', 'HP','LENOVO NEW','LN'),
('DELL SERIES','DS','LENOVO NEW','LN'),
('DELL SERIES','DS','DELL SERIES GEN1','DG'),
('DELL SERIES','DS','DELL SERIES GEN1 NEW','DN'),
('SONY','SO','TOSHIBA','TO'),
('SONY','DS','ACER','AC')

 INSERT #pdt(PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
 VALUES ('APPLE','AP','APPLE','AP') 
 INSERT #pdt(PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
 VALUES ('ACER','AC','APPLE','AP') 

 SELECT * FROM #pdt

ROW_NUM     PRODUCT   PDT_CODE   BKP_PRODUCT   BKP_PDT_CODE
 ----------- ------------------------------ ---------- ---------------------
 --------- ------------
 1           HP          HP         DELL SERIES         DS
 2           HP          HP         LENOVO NEW          LN
 3           DELL SERIES DS         LENOVO NEW          LN
 4           DELL SERIES DS         DELL SERIES GEN1    DG
 5           DELL SERIES DS         DELL SERIES GEN1 NEW  DN
 6           SONY        SO         TOSHIBA             TO
 7           SONY        DS         ACER                AC
 8           APPLE       AP         APPLE               AP
 9           ACER        AC         APPLE               AP              

这里的备份关系是双向的。 HP是DELL SERIES的备份,反之亦然。 我们需要为每个产品创建一个通用备份代码。通过考虑所有备份组合(递归)来创建此备份代码。 备份代码是所有备份的pdt_code的串联。下面是逻辑: 对于惠普,备份是戴尔系列。 但DELLSERIES已将LENOVO NEW,DELL SERIES GEN1,DELL SERIES GEN1作为备份。 因此,HP的备份代码为:HP + DS + LN + DG + DN = HPDSLNDGDN

对于DELL SERIES,备份是LENOVO NEW(第3行)。但是从第4,5行开始,我们还有备份DELL SERIES GEN1,DELL SERIES GEN1 NEW。 DELL SERIES本身也是HP的备份(第1行) 所以DELL SERIES的备份代码与上面相同(因为所有涉及,顺序无关紧要)= HPDSLNDGDN

同样,我们需要为所有其他产品生成backup_code 动态。代码串联的备份代码顺序无关紧要。

注意: 对于Apple来说,备份代码只是AP广告,因为产品和bkp_product都是相同的。 我正在使用SQL Server 2012。

``     以下是预期结果:

ROW_NUM  PRODUCT           PDT_CODE BKP_PRODUCT     BKP_PDT_CODE BACKUP_CODE
----------- ------------------------------ ---------- ----------------------
1        HP                HP       DELL SERIES              DS HPDSLNDGDN
2        HP                HP       LENOVO NEW               LN HPDSLNDGDN
3        DELL SERIES       DS       LENOVO NEW               LN HPDSLNDGDN
4        DELL SERIES       DS       DELL SERIES GEN1         DG HPDSLNDGDN
5        DELL SERIES       DS       DELL SERIES GEN1 NEW     DN HPDSLNDGDN
6        SONY              SO       TOSHIBA                  TO SOTOACAP
7        SONY              DS       ACER                     AC SOTOACAP
8        APPLE             AP       APPLE                    AP AP
9        ACER              AC       APPLE                    AP ACAPSOTO

请帮我动态生成代码。

非常感谢。

1 个答案:

答案 0 :(得分:0)

我相信,@ vladimir-baranov是正确的。这是一个产生答案的查询。但是,结果与您声明的预期结果不一致。

我认为根据源数据,您的预期结果是不正确的。例如,在第9行中,您有一个生成的ACAPSOTO代码。但是PDT_CODE =' TO'没有行,所以图表会崩溃。

同样,由于DS与AC(第7行)之间存在关系,因此大多数备份代码最终都会涉及到ACAP'

无论如何 - 你走了。我已经开始使用您的表生成代码了。然后它几乎是来自@ Vladimir的解决方案的直接剪切/粘贴,最后连接到源表。

if object_id('tempdb..#pdt') is not null
DROP TABLE #pdt
create table #pdt(ROW_NUM INT IDENTITY(1,1),  PRODUCT NVARCHAR(30), PDT_CODE 
NVARCHAR(10),BKP_PRODUCT   NVARCHAR(30),BKP_PDT_CODE NVARCHAR(10))

INSERT #pdt( PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
VALUES ('HP','HP','DELL SERIES','DS'),('HP', 'HP','LENOVO NEW','LN'),
('DELL SERIES','DS','LENOVO NEW','LN'),
('DELL SERIES','DS','DELL SERIES GEN1','DG'),
('DELL SERIES','DS','DELL SERIES GEN1 NEW','DN'),
('SONY','SO','TOSHIBA','TO'),
('SONY','DS','ACER','AC')

 INSERT #pdt(PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
 VALUES ('APPLE','AP','APPLE','AP') 
 INSERT #pdt(PRODUCT,PDT_CODE, BKP_PRODUCT,BKP_PDT_CODE )
 VALUES ('ACER','AC','APPLE','AP') 


 ;
 WITH
CTE_Idents
AS
(
    SELECT PDT_CODE AS Ident
    FROM #pdt

    UNION

    SELECT BKP_PDT_CODE AS Ident
    FROM #pdt
)
,CTE_Pairs
AS
(
    SELECT PDT_CODE as Ident1, BKP_PDT_CODE as Ident2
    FROM #pdt
    WHERE PDT_CODE <> BKP_PDT_CODE

    UNION

    SELECT BKP_PDT_CODE AS Ident1, PDT_CODE AS Ident2
    FROM #pdt
    WHERE PDT_CODE <> BKP_PDT_CODE
)
,CTE_Recursive
AS
(
    SELECT
        CAST(CTE_Idents.Ident AS varchar(8000)) AS AnchorIdent 
        , Ident1
        , Ident2
        , CAST(',' + Ident1 + ',' + Ident2 + ',' AS varchar(8000)) AS IdentPath
        , 1 AS Lvl
    FROM 
        CTE_Pairs
        INNER JOIN CTE_Idents ON CTE_Idents.Ident = CTE_Pairs.Ident1

    UNION ALL

    SELECT 
        CTE_Recursive.AnchorIdent 
        , CTE_Pairs.Ident1
        , CTE_Pairs.Ident2
        , CAST(CTE_Recursive.IdentPath + CTE_Pairs.Ident2 + ',' AS varchar(8000)) AS IdentPath
        , CTE_Recursive.Lvl + 1 AS Lvl
    FROM
        CTE_Pairs
        INNER JOIN CTE_Recursive ON CTE_Recursive.Ident2 = CTE_Pairs.Ident1
    WHERE
        CTE_Recursive.IdentPath NOT LIKE CAST('%,' + CTE_Pairs.Ident2 + ',%' AS varchar(8000))
)
,CTE_RecursionResult
AS
(
    SELECT AnchorIdent, Ident1, Ident2
    FROM CTE_Recursive
)
,CTE_CleanResult
AS
(
    SELECT AnchorIdent, Ident1 AS Ident
    FROM CTE_RecursionResult

    UNION

    SELECT AnchorIdent, Ident2 AS Ident
    FROM CTE_RecursionResult
)
SELECT
    CTE_Idents.Ident
    ,CASE WHEN CA_Data.XML_Value IS NULL 
    THEN CTE_Idents.Ident ELSE CA_Data.XML_Value END AS GroupMembers
    ,DENSE_RANK() OVER(ORDER BY 
        CASE WHEN CA_Data.XML_Value IS NULL 
        THEN CTE_Idents.Ident ELSE CA_Data.XML_Value END
    ) AS GroupID
into #Groups
FROM
    CTE_Idents
    CROSS APPLY
    (
        SELECT CTE_CleanResult.Ident+','
        FROM CTE_CleanResult
        WHERE CTE_CleanResult.AnchorIdent = CTE_Idents.Ident
        ORDER BY CTE_CleanResult.Ident FOR XML PATH(''), TYPE
    ) AS CA_XML(XML_Value)
    CROSS APPLY
    (
        SELECT CA_XML.XML_Value.value('.', 'NVARCHAR(MAX)')
    ) AS CA_Data(XML_Value)
WHERE
    CTE_Idents.Ident IS NOT NULL
ORDER BY Ident;

select #pdt.*, 
        case 
        when #pdt.PDT_CODE = #pdt.BKP_PDT_CODE then #pdt.PDT_CODE 
        else replace(#Groups.GroupMembers, ',', '') end BACKUP_CODE
from #pdt
    join #Groups
    on #pdt.PDT_CODE = #Groups.Ident

结果:

ROW_NUM     PRODUCT         PDT_CODE   BKP_PRODUCT            BKP_PDT_CODE BACKUP_CODE
----------- --------------- ---------- ---------------------- ------------ --------------
1           HP              HP         DELL SERIES            DS           ACAPDGDNDSHPLN
2           HP              HP         LENOVO NEW             LN           ACAPDGDNDSHPLN
3           DELL SERIES     DS         LENOVO NEW             LN           ACAPDGDNDSHPLN
4           DELL SERIES     DS         DELL SERIES GEN1       DG           ACAPDGDNDSHPLN
5           DELL SERIES     DS         DELL SERIES GEN1 NEW   DN           ACAPDGDNDSHPLN
6           SONY            SO         TOSHIBA                TO           SOTO
7           SONY            DS         ACER                   AC           ACAPDGDNDSHPLN
8           APPLE           AP         APPLE                  AP           AP
9           ACER            AC         APPLE                  AP           ACAPDGDNDSHPLN