T-SQL

UNPIVOT()

PIVOT() a UNPIVOT() jsou velmi užitečné funkce jazyka T-SQL. My si dnes ukážeme použití UNPIVOT() funkce na příkladu s objednávkami a jejich cenou a daňovou sazbou. K tomu přidáme i ukázku toho, jak využít možnosti řetězení UNPIVOT() funkcí v jedno dotazu a jak šikovným trikem dosáhnout požadovaného výstupu. A jako bonus si přidáme SUM() s OVER().

Nejprve si vytvoříme zkušební tabulku a naplníme ji daty:

CREATE TABLE [dbo].[SampleTable] (
	[Order_ID] INT NOT NULL PRIMARY KEY,
	[Amount1] MONEY NOT NULL DEFAULT(0),
	[VAT1] MONEY NOT NULL DEFAULT(0),
	[Amount2] MONEY NOT NULL DEFAULT(0),
	[VAT2] MONEY NOT NULL DEFAULT(0),
	[Amount3] MONEY NOT NULL DEFAULT(0),
	[VAT3] MONEY NOT NULL DEFAULT(0)
)
GO

INSERT INTO [dbo].[SampleTable] 
	( [Order_ID], [Amount1], [VAT1], [Amount2], [VAT2], [Amount3], [VAT3] )
	VALUES ( 1, 100, 5, 200, 21, 300, 10),
		   ( 2, 100, 5, 0, 0, 300, 10),
		   ( 3, 500, 5, 600, 7, 700, 10)
GO

SELECT * FROM [dbo].[SampleTable]
GO

V tabulce máme tři objednávky a pro každou z nich máme tři různé částky a tři různé sazby. Aplikace nám to ukládá takto poněkud nešťastně do řádku a my chceme data dostat do podoby, kdy s nimi budeme moci pracovat jako s klasickým datovým setem a počítat celkové částky za objednávky nebo celou jejich množinu.

S pomocí UNPIVOT() můžeme v nejjednodušší možné variantě otočit jednoduše částky ze sloupců do řádků:

SELECT [Order_ID], [Amount], [Amount_Value]
FROM [dbo].[SampleTable]  
	UNPIVOT ([Amount_Value] FOR [Amount] IN ([Amount1], [Amount2], [Amount3])) AS [Amount_Value]  
GO

Nyní vidíme, že se nám každá objednávka objevila třikrát a jednotlivé částky máme místo na řádcí ve sloupcích. Stejným způsobem bychom rádi otočili i hdnoty VAT. Toho může dosáhnout buď pomocí vnořených dotazů, kdy si uděláme stejný dotaz se sloupci pro VAT a připojíme ho joinem přes Order_ID k prvnímu dotazu. Ale může to zkusit i vícenásobným použitím UNPIVOT() funkce v rámci jedno dotazu:

SELECT [Order_ID], [Amount], [Amount_Value], [VAT], [VAT_Value] 
FROM [dbo].[SampleTable]  
	UNPIVOT ([Amount_Value] FOR [Amount] IN ([Amount1], [Amount2], [Amount3])) AS [Amount_Value]  
	UNPIVOT ([VAT_Value]	FOR [VAT] IN ([VAT1], [VAT2], [VAT3])) AS [VAT_Value]
GO

To ale není úplně výsledek, který bychom si přáli. Mezi UNPIVOT() funkcemi totiž není žádná vazba a došlo tak ke kartézkému součinu jejich množin. Můžeme vše ale velmi jednoduše napravit s použitím chytrého filtru ve WHERE:

SELECT [Order_ID], [Amount], [Amount_Value], [VAT], [VAT_Value] 
FROM [dbo].[SampleTable]  
	UNPIVOT ([Amount_Value] FOR [Amount] IN ([Amount1], [Amount2], [Amount3])) AS [Amount_Value]  
	UNPIVOT ([VAT_Value]	FOR [VAT] IN ([VAT1], [VAT2], [VAT3])) AS [VAT_Value]
WHERE RIGHT([Amount], 1) = RIGHT([VAT], 1)
GO

To už je výsledek, který nám udělá radost. Ve WHERE jsme k sobě podle posledního znaku (čísla v názvu sloupce) jednoduše připojili správné Amount a VAT. Je to trošku trik, který nám funguje, protože jsme na chování UNPIVOT() funkce při jejím vícenásobném použití mysleli již při návrhu tabulky.

A když už máme data takto pěkně otočené z řádků do sloupců, můžeme si pěkně spočítat nějaké ty celkové částky:

SELECT 
	*,
	SUM([Amount_Value]) OVER (PARTITION BY [ord].[Order_ID]) [Sum_AMT_Order],
	SUM([Amount_Value]) OVER () [Sum_AMT_Total]
FROM (
		SELECT [Order_ID], [Amount], [Amount_Value], [VAT], [VAT_Value] 
		FROM [dbo].[SampleTable]  
			UNPIVOT ([Amount_Value] FOR [Amount] IN ([Amount1], [Amount2], [Amount3])) AS [Amount_Value]  
			UNPIVOT ([VAT_Value]	FOR [VAT] IN ([VAT1], [VAT2], [VAT3])) AS [VAT_Value]
		WHERE RIGHT([Amount], 1) = RIGHT([VAT], 1)
	) [ord]
GO

Více o použítí UNPIVOT() funkce najde v MSDN.

Leave a Reply

Your email address will not be published. Required fields are marked *