在SQL中将水平表转换为垂直表

时间:2018-07-23 16:29:33

标签: sql sql-server pivot pivot-table

我具有以下表格格式:

enter image description here

我需要将此表转换为以下格式:

enter image description here

我在其他问题中查找了PIVOT函数,但是输入表中的“键”值不是一组固定值,它们可以是任何值。我也在寻找其他类似的问题,但我不确定该如何编写查询。

我的代码是:

SELECT
    ROW_NUMBER () OVER (
ORDER BY RouteCode) AS SrNo
, RouteCode AS X
, SUM(Units) AS Y
FROM
    [ INTERFACE_ok ] .[ dbo ] .[ v_A40OrdersBhQt ]
WHERE [ DeliveryDate ] > CAST(
        FLOOR(CAST(GETDATE () AS FLOAT)) AS DATETIME
    )
    AND CustomerCode LIKE '900%'
GROUP BY [ RouteCode ]

任何帮助将不胜感激,谢谢!

2 个答案:

答案 0 :(得分:3)

您正在寻找动态枢纽

主要步骤如下

  1. 声明变量@sqlX@sqlY以携带您的MAX函数和CASW WHEN表达式以创建XY枢轴列号。
  2. 使用CONCAT组合您的SUM函数和CASW WHEN表达式字符串和主选择字符串以及UNION ALL @sqlX@sqlY查询字符串。
  3. 使用EXECUTE函数动态执行SQL。

TestDLL

CREATE TABLE T(
  SrNo INT,
  X  VARCHAR(100),
  Y INT
);

INSERT INTO T VALUES (1,'N1',100);
INSERT INTO T VALUES (2,'N2',200);
INSERT INTO T VALUES (3,'N3',300);
INSERT INTO T VALUES (4,'N4',400);
INSERT INTO T VALUES (5,'N5',500);
INSERT INTO T VALUES (6,'N6',600);
INSERT INTO T VALUES (7,'N7',700);

这是mysql示例。

SET @sqlX = NULL;
SET @sqlY = NULL;
SET @sql = NULL;

SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(CASE WHEN SrNo =',
      SrNo,
      ' THEN X END) '
    )
  ) INTO @sqlX
FROM T;

SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(CASE WHEN SrNo =',
      SrNo,
      ' THEN Y END) '
    )
  ) INTO @sqlY
FROM T;

SET @sql = CONCAT('SELECT ''X'', ', @sqlX, ' 
                   FROM T
                   UNION ALL
                   SELECT ''Y'', ', @sqlY, '
                   FROM T
                   ');


PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Mysql sqlfiddle

SQL-Server版本

DECLARE @sqlX VARCHAR(MAX)
DECLARE @sqlY VARCHAR(MAX)
DECLARE @sql VARCHAR(MAX)

SET @sqlX = STUFF((SELECT distinct ', CAST( MAX(CASE WHEN SrNo =' + CAST(SrNo AS VARCHAR(5)) +  ' THEN X END) AS VARCHAR(MAX)) '
            FROM T
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');

SET @sqlY = STUFF((SELECT distinct ',CAST( MAX(CASE WHEN SrNo = ' + CAST(SrNo AS VARCHAR(5)) +  ' THEN Y END) AS VARCHAR(MAX)) '
            FROM T
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');

set @sql = CONCAT('SELECT ''X'', ', @sqlX, ' 
                   FROM T
                   UNION ALL
                   SELECT ''Y'', ', @sqlY, '
                   FROM T');

execute(@sql)

sqlserver sqlfiddle

结果

    | X | MAX(CASE WHEN SrNo =1 THEN X END) | MAX(CASE WHEN SrNo =2 THEN X END) | MAX(CASE WHEN SrNo =3 THEN X END) | MAX(CASE WHEN SrNo =4 THEN X END) | MAX(CASE WHEN SrNo =5 THEN X END) | MAX(CASE WHEN SrNo =6 THEN X END) | MAX(CASE WHEN SrNo =7 THEN X END) |
    |---|-----------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|
    | X |                                N1 |                                N2 |                                N3 |                                N4 |                                N5 |                                N6 |                                N7 |
    | Y |                               100 |                               200 |                               300 |                               400 |                               500 |                               600 |                               700 |

注意T可以代替您的子查询或当前结果集。

答案 1 :(得分:1)

亚伦·伯特兰(Aaron Bertrand)写了一篇适合您需要的文章: https://www.mssqltips.com/sqlservertip/2783/script-to-create-dynamic-pivot-queries-in-sql-server/

USE tempdb;
GO
CREATE TABLE dbo.Products
(
  ProductID INT PRIMARY KEY,
  Name      NVARCHAR(255) NOT NULL UNIQUE
  /* other columns */
);
INSERT dbo.Products VALUES
(1, N'foo'),
(2, N'bar'),
(3, N'kin');
CREATE TABLE dbo.OrderDetails
(
  OrderID INT,
  ProductID INT NOT NULL
    FOREIGN KEY REFERENCES dbo.Products(ProductID),
  Quantity INT
  /* other columns */
);
INSERT dbo.OrderDetails VALUES
(1, 1, 1),
(1, 2, 2),
(2, 1, 1),
(3, 3, 1);

他的动态解决方案是将一些东西应用于PIVOT语句:

编写一个收集列的子查询:

DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns += N', p.' + QUOTENAME(Name)
  FROM (SELECT p.Name FROM dbo.Products AS p
  INNER JOIN dbo.OrderDetails AS o
  ON p.ProductID = o.ProductID
  GROUP BY p.Name) AS x;

然后创建您的可执行SQL:

SET @sql = N'
SELECT ' + STUFF(@columns, 1, 2, '') + '
FROM
(
  SELECT p.Name, o.Quantity
   FROM dbo.Products AS p
   INNER JOIN dbo.OrderDetails AS o
   ON p.ProductID = o.ProductID
) AS j
PIVOT
(
  SUM(Quantity) FOR Name IN ('
  + STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')
  + ')
) AS p;';
PRINT @sql;

最后运行它:

EXEC sp_executesql @sql;