SQL Server:将结果从一对多查询展平

时间:2014-01-08 17:43:38

标签: sql sql-server sql-server-2008 one-to-many flatten

好的,我有一个输入订单的系统,每个订单都可以附加任意数量的债券,工作和项目编号。 OrderDetails表包含orderID用于转到OrderNumbers表并获取此订单的所有附加“数字”,然后根据此处找到的numType列,它转到三个表中的一个(债券,工作,项目)并检索为用户显示和打印的实际数字。

这意味着,如果该订单附加了3个项目编号,那么尝试查询订单以搜索某个项目编号可以为同一订单返回3行。

我希望能够返回订单只有1行的查询结果,以及包含分隔形式的所有3个项目的列,这可能吗?

这是我当前的查询,当订单附加多个相同类型的数字时,每个订单返回多行...

SELECT 
    ISNULL(b.Descr,'') as Bond, ISNULL(PO.Description,'') as PO, 
    ISNULL(j.Descr,'') as Job, ISNULL(Proj.Descr,'') as Project, 
    d.OrdNbr, d.BillofLadingNbr, d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
LEFT JOIN 
    OrderItemNumbers n ON d.OWID = n.LoadOWID 
LEFT JOIN 
    Bond b ON n.NumberOWID = b.OWID AND n.NumType = 0
LEFT JOIN 
    PONumbers PO ON n.NumberOWID = PO.OWID AND n.NumType = 1 
LEFT JOIN 
    Job j ON n.NumberOWID = j.OWID AND n.NumType = 2 
LEFT JOIN 
    Project Proj ON n.NumberOWID = Proj.OWID AND n.NumType = 3 
WHERE 
    d.OWID = 'BTBD1004'

以下是我得到的结果......

Bond    PO              Job Project         OrdNbr      BillofLadingNbr
                            82001-8177-44   BTBD000063  BTBD000072  
        4.158 Kingsport                     BTBD000063  BTBD000072  
                            IME-81-1(118)   BTBD000063  BTBD000072  

这就是我想要的......(基本上是扁平化前4列)

Bond    PO              Job Project                         OrdNbr      BillofLadingNbr
        4.158 Kingsport     82001-8177-44, IME-81-1(118)    BTBD000063  BTBD000072  

任何帮助表示赞赏! 谢谢, 道格

1 个答案:

答案 0 :(得分:6)

根据您的示例,似乎BondJob表上没有匹配的记录。如果有,你会得到这样的东西:

BOND        PO              JOB      PROJECT        ORDNBR     BILLOFLADINGNBR  
Some bond                                           BTBD000063 BTBD000072
            4.158 Kingsport                         BTBD000063 BTBD000072
                            Some job                BTBD000063 BTBD000072
                                     82001-8177-44  BTBD000063 BTBD000072
                                     IME-81-1(118)  BTBD000063 BTBD000072

该结果集似乎“稀疏”,因为您将OrderItemNumbers直接加入到主查询中:此表为每个子表都有一个不同的行(PONumbersJob,{ {1}}),因此您将每个联接放在一个不同的分隔行中。为了避免这种情况,您可以将Project加入每个子表,然后将此子查询加入OrderItemNumbers(通过共享OrderDetail):

LoadOWID

这样你就会得到以下结果:

SELECT
    ISNULL(b.Descr, '') as Bond,
    ISNULL(PO.Description, '') as PO, 
    ISNULL(j.Descr, '') as Job,
    ISNULL(Proj.Descr, '') as Project, 
    d.OrdNbr,
    d.BillofLadingNbr,
    d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Bond aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 0
    ) AS b
        ON b.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM PONumbers aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 1
    ) AS PO
        ON PO.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Job aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 2
    ) AS j
        ON j.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Project aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 3
    ) AS Proj
        ON Proj.LoadOWID = d.OWID
WHERE 
    d.OWID = 'BTBD1004'

我同意这不完全是你所要求的。您似乎还需要在Bond PO Job Project OrdNbr BillofLadingNbr Some bond 4.158 Kingsport Some job 82001-8177-44 BTBD000063 BTBD000072 Some bond 4.158 Kingsport Some job IME-81-1(118) BTBD000063 BTBD000072 表(至少)上进行某种“部分转动”。

从理论上讲,这也是可能的,但我不会这样做。这显然是一个表示要求,在SQL层上实现它的成本太高了。

修改

为了获得完整的平面结果集,您可以这样做(基于this SO answer):

Project

这将导致:

DECLARE @bonds   VARCHAR(MAX)
DECLARE @numbers VARCHAR(MAX)
DECLARE @jobs    VARCHAR(MAX)
DECLARE @projs   VARCHAR(MAX)

DECLARE @owid VARCHAR(10) = 'BTBD1004'

SELECT
    @bonds = COALESCE(@bonds + ', ', '') + aux.Descr
FROM
    Bond aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 0
WHERE
    n.LoadOWID = @owid

SELECT
    @numbers = COALESCE(@numbers + ', ', '') + aux.Description
FROM
    PONumbers aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 1
WHERE
    n.LoadOWID = @owid

SELECT
    @jobs = COALESCE(@jobs + ', ', '') + aux.Descr
FROM
    Job aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 2
WHERE
    n.LoadOWID = @owid

SELECT
    @projs = COALESCE(@projs + ', ', '') + aux.Descr
FROM
    Project aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 3
WHERE
    n.LoadOWID = @owid

SELECT
    @bonds,
    @numbers, 
    @jobs,
    @projs,
    d.OrdNbr,
    d.BillofLadingNbr,
    d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
WHERE 
    d.OWID = @owid

如果您为同一Bond PO Job Project OrdNbr BillofLadingNbr Some bond 4.158 Kingsport Some job 82001-8177-44, IME-81-1(118) BTBD000063 BTBD000072 获得许多债券x许多PO的x多个工作,它就有效。

IMHO很好地说明了我试图说的话:是的,它可以满足您的需要,但它实际上是丑陋的。当然,如果你不在乎,继续(只需添加一些触发器,你会觉得它再次是1995年:P)。

希望它有所帮助。