2010-05-27 15 views
20

Est-il réellement possible de faire pivoter un T-SQL (2005) pour que (par souci d'argument) les valeurs des lignes de la première colonne deviennent les titres des colonnes de la table de sortie? Je réalise que ce n'est pas vraiment ce à quoi sert PIVOT, mais c'est ce dont j'ai besoin - la possibilité de demander une table où les colonnes ne sont pas connues auparavant car elles ont été entrées en tant que valeurs dans une table.Pivot T-SQL? Possibilité de créer des colonnes de table à partir des valeurs de ligne

Même un hack serait bien, tbh.

Répondre

24

Itzik exemple de Ben-Gan sur la façon de construire PIVOT dynamique, je recommande fortement son Inside Microsoft SQL Server 2008: T-SQL Programming livre

-- 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 
+0

La 2ème à la dernière ligne doit juste être EXEC (@sql) –

+0

@JohnnyBones pourquoi penseriez-vous cela? 'EXEC sp_executesql @ sql' est la syntaxe correcte. il échouera si vous essayez 'EXEC (@sql)' parce qu'il essayera de trouver la procédure stockée avec ce texte. –

5

Une requête de pivot légèrement mieux est la suivante:

-- 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; 

Je préfère le style de une expression de table commune (CTE) sur une table dérivée car je pense que c'est plus facile à comprendre. Itzik aussi, comme il recommande ce style dans son livre Querying Microsoft SQL Server 2012.