使用父信息将子记录嵌入单行

时间:2017-11-16 16:42:33

标签: sql sql-server database rdbms

我有以下表格

BATCH

BatchID     Name      CustomerID  DateCreated  Status
12           A         1           01/01/2013   Active
13           B         12          01/01/2013   Inactive
14           C         245         01/01/2013   Complete

BATCHDETAIL

BatchDetailID  BatchID  Weight  Price    DestinationCode
1              12       55      500.00   99
2              12       119     1500.00  55
3              13       12      133      1212

批记录可以通过FK BatchDetail.BatchID

链接许多批次明细记录

我想编写一个查询来选择返回给用户的单行,该行将BATCH记录中的信息与BATCLDETAIL记录中的Weight,Price和DestinationCode结合起来,用于BatchID = 12

所以输出结果为:

BatchID      Name      CustomerID  DateCreated  Status  WeightA PriceA  DestinationCodeA  WeightB  PriceB   DestinationCodeB
12           A         1           01/01/2013   Active  55      500.00  99                119       1500      55

所以你可以看到我希望有一行将所有信息组合在一行中,并用A或B区分每个细节记录(假设最多只允许2条细节记录)

我想过用这些字段创建一个表,然后在一系列select语句中构建信息,最后在临时表上进行选择,但是将查询放到单个SQL块中是理想的。

2 个答案:

答案 0 :(得分:0)

以下是使用动态SQL的解决方案:

-- Get the MAX total number of records per BatchID (how many sets of columns do we need?)

DECLARE @requiredLevels int = (SELECT MAX(C) FROM (SELECT COUNT(*) C FROM BATCHDETAIL GROUP BY BatchID) Q)
;

-- Build a dynamic statement for the final SELECT fields

DECLARE
    @finalFieldsSQL varchar(1000) = ''
    , @finalFieldsN int = 1
;

WHILE @finalFieldsN <= @requiredLevels
BEGIN
    SET @finalFieldsSQL = @finalFieldsSQL + ', Weight' + CHAR(64 + @finalFieldsN) + ', Price' + CHAR(64 + @finalFieldsN) + ', DestinationCode' + CHAR(64 + @finalFieldsN)
    SET @finalFieldsN = @finalFieldsN + 1
END

-- Build a dynamic statement for the subquery SELECT fields

DECLARE
    @subqueryFieldsSQL varchar(1000) = ''
    , @subqueryFieldsN int = 1
;

WHILE @subqueryFieldsN <= @requiredLevels
BEGIN
    SET @subqueryFieldsSQL = @subqueryFieldsSQL + ', MAX([' + CAST(@subqueryFieldsN AS varchar) + ']) ColumnName' + CHAR(64 + @subqueryFieldsN)
    SET @subqueryFieldsN = @subqueryFieldsN + 1
END

-- Build a dynamic statement for the PIVOT fields

DECLARE
    @pivotFieldsSQL varchar(1000) = ''
    , @pivotFieldsN int = 1
;

WHILE @pivotFieldsN <= @requiredLevels
BEGIN
    SET @pivotFieldsSQL = @pivotFieldsSQL + ', [' + CAST(@pivotFieldsN AS varchar) + ']'
    SET @pivotFieldsN = @pivotFieldsN + 1
END

SET @pivotFieldsSQL = SUBSTRING(@pivotFieldsSQL, 3, LEN(@pivotFieldsSQL) - 2)

-- Build the final SQL statement and execute

DECLARE @SQL varchar(8000) =
    '
        SELECT
            B.BatchID, B.Name, B.CustomerID, B.DateCreated, [Status]' + @finalFieldsSQL + '
        FROM
            BATCH B
            LEFT JOIN
                (
                    SELECT
                        BatchID' + REPLACE(@subqueryFieldsSQL, 'ColumnName', 'Weight') + '
                    FROM
                        (
                            SELECT BD.BatchID, [Weight], ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
                            FROM
                                BATCH B
                                JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
                        ) Q
                        PIVOT
                        (
                            MAX([Weight])
                            FOR R IN (' + @pivotFieldsSQL + ')
                        ) P
                    GROUP BY BatchID
                ) W
            ON B.BatchID = W.BatchID
            LEFT JOIN
                (
                    SELECT
                        BatchID' + REPLACE(@subqueryFieldsSQL, 'ColumnName', 'Price') + '
                    FROM
                        (
                            SELECT BD.BatchID, Price, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
                            FROM
                                BATCH B
                                JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
                        ) Q
                        PIVOT
                        (
                            MAX(Price)
                            FOR R IN (' + @pivotFieldsSQL + ')
                        ) P
                    GROUP BY BatchID
                ) P
            ON B.BatchID = P.BatchID
            LEFT JOIN
                (
                    SELECT
                        BatchID' + REPLACE(@subqueryFieldsSQL, 'ColumnName', 'DestinationCode') + '
                    FROM
                        (
                            SELECT BD.BatchID, DestinationCode, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
                            FROM
                                BATCH B
                                JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
                        ) Q
                        PIVOT
                        (
                            MAX(DestinationCode)
                            FOR R IN (' + @pivotFieldsSQL + ')
                        ) P
                    GROUP BY BatchID
                ) D
            ON B.BatchID = D.BatchID
    '

EXEC (@SQL)

如果您不想显示空记录,请在最终声明(3次出现)中将LEFT JOIN替换为JOIN

答案 1 :(得分:0)

您可以使用PivotUnPivot来实现此结果。试试这样的事情:

SELECT 
BatchID,[Name],[CustomerID],[DateCreated],[Status], 
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB

FROM (


        SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
        FROM
        (
          SELECT  b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
        FROM #Batch B
        INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
        ) AS cp
        UNPIVOT 
        (
          Val FOR Col IN ([Weight], [Price], [DestinationCode])
        ) AS up
) AS query

PIVOT (MAX(Val)
      FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]

这只是标准查询。你可以根据自己的喜好使这个脚本动态化:

完整脚本:

Create table #Batch
(BatchID int,
[Name] char(1),
[CustomerID] int,
[DateCreated] date,
[Status] varchar(50)
)

Create table #BATCHDETAIL
(BatchDetailID int,
BatchID int,
[Weight] int,
Price money,
DestinationCode int
)


INSERT INTO #Batch
VALUES(12,'A',1,'01/01/2013','Active')
,(13,'B',12,'01/01/2013','Inactive')
,(14,'C',245,'01/01/2013','Complete')

INSERT INTO #BATCHDETAIL
VALUES(1,12,55,500.00,99)
,(2,12,119,1500.00,55)
,(3,13,12,133,1212)



SELECT 
BatchID,[Name],[CustomerID],[DateCreated],[Status], 
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB

FROM (


        SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
        FROM
        (
          SELECT  b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
        FROM #Batch B
        INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
        ) AS cp
        UNPIVOT 
        (
          Val FOR Col IN ([Weight], [Price], [DestinationCode])
        ) AS up
) AS query

PIVOT (MAX(Val)
      FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]