2010-05-27 13 views
20

È effettivamente possibile ruotare un T-SQL (2005) in modo che (per l'argomento) i valori delle righe della prima colonna diventino i titoli delle colonne della tabella di output?Pivot T-SQL? Possibilità di creare colonne di tabelle da valori di riga

Mi rendo conto che questo non è proprio ciò che PIVOT è, ma è ciò di cui ho bisogno: la possibilità di richiedere una tabella in cui le colonne non sono note in precedenza perché sono state inserite come valori in una tabella.

Anche un trucco sarebbe bello, tbh.

esempio

risposta

24

Itzik Ben-Gan su come costruire PIVOT dinamica, consiglio vivamente il suo Inside Microsoft SQL Server 2008: T-SQL Programming libro

-- 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 2a riga deve essere EXEC (@sql) –

+0

@JohnnyBones perché la pensi? 'EXEC sp_executesql @ sql' è la sintassi corretta. fallirà se proverai 'EXEC (@sql)' perché proverà a trovare la stored procedure con quel testo. –

5

Un po 'meglio di query di rotazione è la seguente:

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

io preferisco lo stile di una Common Table Expression (CTE) su una tabella derivata come penso sia più facile da capire. Anche Itzik lo consiglia, come suggerisce questo stile nel suo libro. Querying Microsoft SQL Server 2012.