【问题标题】:T-SQL Pivot? Possibility of creating table columns from row valuesT-SQL 枢轴?从行值创建表列的可能性
【发布时间】:2011-02-24 18:04:33
【问题描述】:

实际上是否可以旋转 T-SQL (2005) 以便(为了论证)第一列的行的值成为输出表列的标题?

我意识到这并不是 PIVOT 的真正用途,但这正是我所需要的 - 能够请求预先不知道列的表,因为它们已作为值输入到表中。

即使是 hack 也不错,tbh。

【问题讨论】:

    标签: tsql pivot database-table


    【解决方案1】:

    Itzik Ben-Gan 关于如何构建动态 PIVOT 的示例,我强烈推荐他的 Inside Microsoft SQL Server 2008: T-SQL Programming

    -- Creating and Populating the Orders Table
    USE tempdb;
    GO
    
    IF OBJECT_ID('dbo.Orders') IS NOT NULL
    DROP TABLE dbo.Orders;
    GO
    
    CREATE TABLE dbo.Orders
    (
    orderid   int        NOT NULL PRIMARY KEY NONCLUSTERED,
    orderdate datetime   NOT NULL,
    empid     int        NOT NULL,
    custid    varchar(5) NOT NULL,
    qty       int        NOT NULL
    );
    
    CREATE UNIQUE CLUSTERED INDEX idx_orderdate_orderid
    ON dbo.Orders(orderdate, orderid);
    
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(30001, '20020802', 3, 'A', 10);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(10001, '20021224', 1, 'A', 12);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(10005, '20021224', 1, 'B', 20);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(40001, '20030109', 4, 'A', 40);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(10006, '20030118', 1, 'C', 14);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(20001, '20030212', 2, 'B', 12);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(40005, '20040212', 4, 'A', 10);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(20002, '20040216', 2, 'C', 20);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(30003, '20040418', 3, 'B', 15);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(30004, '20020418', 3, 'C', 22);
    INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    VALUES(30007, '20020907', 3, 'D', 30);
    GO
    
    -- Static PIVOT
    SELECT *
    FROM (SELECT custid, YEAR(orderdate) AS orderyear, qty
    FROM dbo.Orders) AS D
    PIVOT(SUM(qty) FOR orderyear IN([2002],[2003],[2004])) AS P;
    GO
    
    -- Dynamic PIVOT
    DECLARE @T AS TABLE(y INT NOT NULL PRIMARY KEY);
    
    DECLARE
    @cols AS NVARCHAR(MAX),
    @y    AS INT,
    @sql  AS NVARCHAR(MAX);
    
    -- Construct the column list for the IN clause
    -- e.g., [2002],[2003],[2004]
    SET @cols = STUFF(
    (SELECT N',' + QUOTENAME(y) AS [text()]
    FROM (SELECT DISTINCT YEAR(orderdate) AS y FROM dbo.Orders) AS Y
    ORDER BY y
    FOR XML PATH('')),
    1, 1, N'');
    
    -- Construct the full T-SQL statement
    -- and execute dynamically
    SET @sql = N'SELECT *
    FROM (SELECT custid, YEAR(orderdate) AS orderyear, qty
    FROM dbo.Orders) AS D
    PIVOT(SUM(qty) FOR orderyear IN(' + @cols + N')) AS P;';
    
    EXEC sp_executesql @sql;
    GO
    

    【讨论】:

    • 倒数第二行只需要 EXEC (@sql)
    • @JohnnyBones 你为什么会这样想? EXEC sp_executesql @sql 是正确的语法。如果您尝试EXEC (@sql),它将失败,因为它将尝试使用该文本查找存储过程。
    【解决方案2】:

    一个稍微好一点的pivot查询如下:

    -- Static PIVOT
    WITH PivotData AS
    (
    SELECT custid, YEAR(orderdate) AS orderyear, qty
    FROM dbo.Orders
    )
    SELECT custid, [2002], [2003], [2004]
    FROM PivotData
    PIVOT(SUM(qty) FOR orderyear IN([2002],[2003],[2004])) AS P;
    

    与派生表相比,我更喜欢公用表表达式 (CTE) 的样式,因为我认为它更容易理解。 Itzik 也是如此,他在他的书 Querying Microsoft SQL Server 2012 中推荐了这种风格。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-23
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多