【问题标题】:Group by column and multiple Rows into One Row multiple columns按列和多行分组为 One Row 多列
【发布时间】:2020-08-21 10:31:44
【问题描述】:

请帮帮我:

我想按 TestType 列分组,但如果 TestType 相同,则 Result 应拆分为列

CREATE TABLE Result(WorkOrder varchar(10), TestType varchar(20), Result decimal(10,2));
INSERT INTO Result (WorkOrder, TestType, Result) VALUES 
('HP19002316','VitaminA', 10.3),
('HP19002316','VitaminA', 11.3),
('HP19002316','VitaminA', 12.3),
('HP19002316','VitaminB', 13.4),
('HP19002316','VitaminB', 14.4),
('HP19002316','VitaminC', 15.5),
('HP19002316','VitaminD', 17.0)

我希望 SQL 以这种格式返回数据

WorkOrder       TestType        Result1   Result2  Result3 
==========================================================
HP19002316      VitaminA        10.3        11.3    12.3    
HP19002316      VitaminB        13.4        14.4    NULL
HP19002316      VitaminC        15.5        NULL    NULL
HP19002316      VitaminD        17.0        NULL    NULL

Result# 列应该是动态的,因为每个 TestType 都有很多结果

【问题讨论】:

  • 回答:如果您希望列数可变,则需要动态 SQL。常规 SQL 无法做到这一点。
  • “我想要”不是问题。你需要什么帮助? 你的尝试没有成功怎么办?你有什么尝试?
  • 另外,您的数据中的什么表示什么是“Result1”、“Result2”和“Result3”?你没有总是升序的键,那么什么决定了“顺序”?
  • 然而,您在此处所追求的称为数据透视表或交叉表(条件聚合)。这很可能与ROW_NUMBER 结合使用。正如@TimBiegeleisen 所建议的那样,如果TestType 的最大行数无法确定,您也需要动态SQL。
  • 谢谢大家,在发帖之前,我尝试过使用动态 SQL 和 pivot 但没有帮助。如果您有任何指导,我们将不胜感激。提前谢谢你。

标签: sql sql-server


【解决方案1】:

正如我在 cmets 中提到的,您需要的是 PIVOT 或交叉表;我更喜欢后者,所以我将使用它。

对此的非动态解决方案如下:

WITH RNs AS(
    SELECT WorkOrder,
           TestType,
           Result,
           ROW_NUMBER() OVER (PARTITION BY WorkOrder, TestType ORDER BY (SELECT NULL)) AS RN --ORDER BY should be your ID/always ascending column
    FROM dbo.Result)
SELECT WorkOrder,
       TestType,
       MAX(CASE RN WHEN 1 THEN Result END) AS Result1,
       MAX(CASE RN WHEN 2 THEN Result END) AS Result2,
       MAX(CASE RN WHEN 3 THEN Result END) AS Result3
FROM RNs R
GROUP BY WorkOrder,
         TestType;

但是,问题在于这会将您“锁定”为 3 个结果,但您建议结果的数量不确定。因此,您需要一个动态的解决方案。

以下最多可得到 100 个结果。如果您确实需要多于多列,则在 CTE Tally 中将更多 CROSS JOINs 添加到 N。这个结果是这样的(相当混乱)。

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10),
        @MaxTally int;

SELECT @MaxTally = MAX(C)
FROM (SELECT COUNT(*) AS C
      FROM dbo.Result
      GROUP BY WorkOrder,
               TestType) R;

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT TOP (@MaxTally) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2) --100 rows, add more Ns for more rows
SELECT @SQL = N'WITH RNs AS(' + @CRLF +
              N'    SELECT WorkOrder,' + @CRLF +
              N'           TestType,' + @CRLF +
              N'           Result,' + @CRLF +
              N'           ROW_NUMBER() OVER (PARTITION BY WorkOrder, TestType ORDER BY (SELECT NULL)) AS RN --ORDER BY should be your ID/always ascending column' + @CRLF +
              N'    FROM dbo.Result)' + @CRLF +
              N'SELECT WorkOrder,' + @CRLF +
              N'       TestType,' + @CRLF +
              --Using FOR XML PATH due to not knowing SQL Server version
              STUFF((SELECT N',' + @CRLF +
                            CONCAT(N'       MAX(CASE RN WHEN ',T.I,N' THEN Result END) AS Result',T.I)
                     FROM Tally T
                     ORDER BY T.I ASC
                     FOR XML PATH(N''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,3,N'') + @CRLF +
              N'FROM RNs R' + @CRLF +
              N'GROUP BY WorkOrder,' + @CRLF +
              N'         TestType;';

PRINT @SQL; --Your best friend.

EXEC sys.sp_executesql @SQL;

【讨论】:

    【解决方案2】:

    你可以试试这个并扩展逻辑。

    select *,((select Result from (select TestType,Result, ROW_NUMBER() over ( PARTITION BY testtype ORDER BY  Testtype) Id from Result) a where a.Id='1' and a.TestType=b.TestType )) Result1,
    ((select Result from (select TestType,Result, ROW_NUMBER() over ( PARTITION BY testtype ORDER BY  Testtype) Id from Result) a where a.Id='2' and a.TestType=b.TestType )) Result2,
    ((select Result from (select TestType,Result, ROW_NUMBER() over ( PARTITION BY testtype ORDER BY  Testtype) Id from Result) a where a.Id='3' and a.TestType=b.TestType )) Result3
     from (
    select Max(WorkOrder) WorkOrder,TestType from Result group by TestType ) b
    

    【讨论】:

    • 对每个结果使用子查询在这里似乎有点过头了。这会导致仅对 3 列的表进行 4 次扫描,并且不会随着添加更多结果而扩展。
    • 非常感谢@Gnyasha,你帮了我一个很好的办法
    • 为什么要使用需要 4 次扫描而不是 1 次的非动态方法,@PeterNguyNguyen?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 2012-01-03
    相关资源
    最近更新 更多