【问题标题】:SQL: Create flattened string from hierarchical (one-to-many) dataSQL:从分层(一对多)数据创建扁平化字符串
【发布时间】:2016-05-09 20:54:11
【问题描述】:

我一直在努力将一些数据转换为如下所示的格式。 我终于做到了,但我很想知道它可以通过什么方式改进。 我的最终解决方案的步骤包括在内。 正确的结果由最终查询产生。 格式: Location1:Date1:Code1,Code2,Code...:Date2:Code1,Code2,Code...:Date...:Code...//Location2:Date...:Code...//地点...:日期...:代码...

结果: Loc1:2016-01-01:Alpha:2016-01-03:Bravo,Charlie,Charlie:2016-01-04:Alpha,Alpha:2016-01-05:Alpha,Delta,Echo//Loc2:2016-01 -01:Bravo,Delta//Loc3:2016-01-02:Bravo

declare @Operation table (
ID int identity not NULL,
ItemID int,
LocationID int,
[Date] date
)

declare @Location table (
ID int identity not NULL,
Name varchar(200)
)

declare @LineItem table (
ID int identity not NULL,
OperationID int,
CodeID int
)

declare @Code table (
ID int identity not NULL,
Value varchar(200)
)

insert @Operation
values
(1, 1, '2016-01-01'),
(1, 2, '2016-01-01'),
(1, 1, '2016-01-03'),
(1, 1, '2016-01-04'),
(1, 3, '2016-01-02'),
(1, 1, '2016-01-05')

insert @Location
values
('Loc1'),
('Loc2'),
('Loc3')

insert @LineItem
values
(1, 1),
(2, 4),
(2, 2),
(3, 3),
(3, 2),
(3, 3),
(4, 1),
(4, 1),
(5, 2),
(6, 5),
(6, 4),
(6, 1)

insert @Code
values
('Alpha'),
('Bravo'),
('Charlie'),
('Delta'),
('Echo')

select
*
from @Operation vo
join @Location vl
on vl.ID = vo.LocationID
join @LineItem vli
on vli.OperationID = vo.ID
join @Code vc
on vc.ID = vli.CodeID

select
vl.Name as OpLocation,
vo.[Date] as OpDate,
OpCodes
from @Operation vo
join @Location vl
on vl.ID = vo.LocationID
cross apply (
select
    stuff(List, 1, 1, '') as OpCodes
from (
    select
        ',' + vc.Value
    from @LineItem vli
    join @Code vc
        on vc.ID = vli.CodeID
    where vli.OperationID = vo.ID
    order by vc.Value
    for xml path('')
    ) as X(List)
) as xCodes

select
vl.Name as OpLocation,
vo.[Date] as OpDate,
OpCodes
from @Operation vo
join @Location vl
on vl.ID = vo.LocationID
cross apply (
select
    stuff(List, 1, 1, '') as OpCodes
from (
    select
        ',' + vc.Value
    from @LineItem vli
    join @Code vc
        on vc.ID = vli.CodeID
    where vli.OperationID = vo.ID
    order by vc.Value
    for xml path('')
    ) as X(List)
) as xCodes
group by vl.Name, vo.[Date], OpCodes

select
vl.Name as OpLocation,
OpDateAndCodes
from @Operation vo
join @Location vl
on vl.ID = vo.LocationID
cross apply (
select
    convert(varchar(10), [Date], 21) + ':' + OpCodes as OpDateAndCodes
from (
    select
        stuff(List, 1, 1, '') as OpCodes
    from (
        select
            ',' + vc.Value
        from @LineItem vli
        join @Code vc
            on vc.ID = vli.CodeID
        where vli.OperationID = vo.ID
        order by vc.Value
        for xml path('')
        ) as X(List)
    ) as Codes
) as xODC
group by vl.Name, OpDateAndCodes

;with cte as (
select
    vl.Name as Location,
    OpDateAndCodes
from @Operation vo
join @Location vl
    on vl.ID = vo.LocationID
cross apply (
    select
        convert(varchar(10), [Date], 21) + ':' + OpCodes as OpDateAndCodes
    from (
        select
            stuff(List, 1, 1, '') as OpCodes
        from (
            select
                ',' + vc.Value
            from @LineItem vli
            join @Code vc
                on vc.ID = vli.CodeID
            where vli.OperationID = vo.ID
            order by vc.Value
            for xml path('')
            ) as X(List)
        ) as Codes
    ) as xODC
)
select
    Location,
    stuff(List, 1, 1, '') as ODC
from cte cte1
cross apply (
    select
        ':' + OpDateAndCodes
    from cte cte2
    where cte2.Location = cte1.Location
    for xml path('')
    ) as X(List)

//SOLUTION
;with cte as (
select
    vl.Name as Location,
    OpDateAndCodes
from @Operation vo
join @Location vl
    on vl.ID = vo.LocationID
cross apply (
    select
        convert(varchar(10), [Date], 21) + ':' + OpCodes as OpDateAndCodes
    from (
        select
            stuff(List, 1, 1, '') as OpCodes
        from (
            select
                ',' + vc.Value
            from @LineItem vli
            join @Code vc
                on vc.ID = vli.CodeID
            where vli.OperationID = vo.ID
            order by vc.Value
            for xml path('')
            ) as X(List)
        ) as Codes
    ) as xODC
)
select
    distinct
        stuff(List, 1, 2, '')
from cte cte1
cross apply (
    select
        '//' + OpLocationDateAndCodes
    from (
        select
            distinct
                Location + ':' + stuff(List, 1, 1, '') as OpLocationDateAndCodes
        from cte cte2
        cross apply (
            select
                ':' + OpDateAndCodes
            from cte cte3
            where cte3.Location = cte2.Location
            for xml path('')
            ) as X(List)
        ) as OLDC
    for xml path('')
    ) as X(List)

【问题讨论】:

    标签: sql sql-server common-table-expression cross-apply for-xml-path


    【解决方案1】:

    这是我想出的一种方法,它使用带有子查询的单个查询。输出与您的代码完全匹配。

    SELECT STUFF((
        SELECT
            '//' + L.Name
            + (
                SELECT ':' + CONVERT(VARCHAR(50), O.Date) + ':'
                    + STUFF((
                        SELECT ',' + C.Value
                        FROM @LineItem LI
                            INNER JOIN @Code C
                                ON C.ID = LI.CodeID
                        WHERE LI.OperationID = O.ID
                        FOR XML PATH('')), 1, 1, '')
                FROM @Operation O
                WHERE O.LocationID = L.ID
                FOR XML PATH('')
            )
        FROM @Location L
        FOR XML PATH('')
    ), 1, 2, '')
    

    【讨论】:

    • 感谢您的意见;我会看的;我可以通过删除别名为 cte1 的级别来简化它;无论如何它现在都在生产中,并且似乎运行良好(没有抱怨);再次感谢,顺便说一句,很高兴看到我的问题不再被否决;我发现 stackoverflow 非常有用,但不理解强迫性,无论个人可能明显优于立即投反对票,但与外观相反,我似乎花了很多时间来解决自己的问题并询问以一种清晰而简单的方式......
    • 我了解挑战 - 我很久以前就学会了不要让少数人的傲慢毁了其他人。我当然很欣赏你的例子的详细程度。随时为您认为有用的任何答案投票;-)
    猜你喜欢
    • 2019-03-15
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 2019-01-05
    • 1970-01-01
    • 2020-03-14
    相关资源
    最近更新 更多