I min tidigare artikel om den grundläggande pivotoperatorn såg vi hur pivotoperatorn kunde användas för att konvertera rader till kolumner, vilket resulterade i pivottabeller. Vi såg att det fanns tre huvudsteg för att skapa en pivottabell. Det första steget var att välja basdata. Det andra steget var att konvertera basdata till ett tabellvärde uttryck, och det sista steget innebar att tillämpa en pivotoperator på de temporära data, vilket resulterade i pivottabellen.
Ta en titt på exemplet nedan.
USE schooldb SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN ([London],[Liverpool],[Leeds],[Manchester]) ) AS StudentPivotTable
Obs! För att skapa dummydatabasen och data, se föregående artikel om pivotoperatören.
Begränsningar för pivotoperatör
Det finns dock vissa begränsningar för pivotoperatören. Inuti pivotoperatorn måste vi ange det aggregerade fältet och de kolumner som vi vill pivotera vår data på. Slutligen måste vi också ställa in de individuella värdena för de kolumnrubriker vi vill skapa.
Om vi körde skriptet från föregående avsnitt skulle vi få följande resultat:
[tabell id=35 /]
Kolumnernas rubriker är de individuella värdena i stadskolumnen. Vi angav dessa värden i pivotoperatorn i vår fråga.
Den tråkigaste delen av att skapa pivottabeller är att specificera värdena för kolumnrubrikerna manuellt. Det här är den del som är utsatt för de flesta fel, särskilt om data i din onlinedatakälla ändras. Vi kan inte vara säkra på att värdena vi angav i pivotoperatorn kommer att finnas kvar i databasen tills vi skapar denna pivottabell nästa gång.
I vårt manus angav vi till exempel London, Liverpool, Leeds och Manchester som värden för rubrikerna i vår pivottabell. Dessa värden fanns i kolumnen Сity i elevtabellen. Vad händer om ett eller flera av dessa värden på något sätt raderas eller uppdateras? I sådana fall kommer null att returneras.
Ett bättre tillvägagångssätt skulle vara att skapa en dynamisk fråga som returnerar en fullständig uppsättning värden från kolumnen från vilken du försöker generera din pivottabell.
Skapa en dynamisk pivottabell
I det här avsnittet kommer vi att se hur man skapar en dynamisk pivottabell.
Det betyder att vi inte behöver ange värdena manuellt för den kolumn som vi försöker generera vår pivottabell från. Istället kommer vi att ställa in dessa värden dynamiskt. För detta ändamål kommer vi att använda funktionen "QUOTENAME".
Som alltid, se till att du är väl säkerhetskopierad innan du experimenterar med en ny kod. Se den här artikeln om säkerhetskopiering av MS SQL-databaser om du är osäker.
QUOTENAME-funktion
Funktionen "QUOTENAME" formaterar valda resultat. Innan du förklarar dynamisk pivot är det värt att titta på ett snabbt fungerande exempel på "QUOTENAME"-funktionen.
Ta en titt på följande fråga.
USE schooldb SELECT QUOTENAME(city)+ ',' FROM student
Som standard omsluter funktionen "QUOTENAME" de valda objekten med hakparenteser. Utdata från ovanstående fråga ser ut så här:
[tabell id=36 /]
Lagra kolumnnamn i en variabel
Även om vi har raderat kolumnvärdena med hakparenteser, måste vi ange värdena i pivotoperatorn i detta format:
”[Leeds],[Liverpool],[London],[Manchester]”
För att göra detta behöver vi en variabel.
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES PRINT @CityNames
I ovanstående fråga deklarerade vi en variabel "@CityNames" och initierade den med en tom sträng. Sedan använde vi en SELECT-sats för att välja distinkta stadsnamn från stadskolumnen och lagra dem iterativt i variabeln "@CityNames". I varje iteration läggs ett distinkt värde i stadskolumnen tillsammans med ett kommatecken till variabeln "@CityNames".
Sedan skrev vi ut värdet som lagrats i denna variabel. Resultatet av ovanstående fråga kommer att se ut så här:
"[Leeds],[Liverpool],[London],[Manchester],"
Om du tittar på utdata finns det ett kommatecken efter det sista värdet. Det behöver vi inte.
Ta bort ett efterföljande komma
För att ta bort ett avslutande kommatecken använder vi en VÄNSTER-funktion som tar en sträng som sitt första argument. Det andra argumentet är antalet tecken som ska returneras från den strängen med början från det första tecknet. Ta en titt på följande fråga:
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) PRINT @CityNames
Var uppmärksam på denna rad i skriptet:
SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1)
På den här raden i skriptet använde vi funktionen VÄNSTER för att få alla tecken på vänster sida av värdet lagrat i variabeln "@CityNames", med början från det första elementet. I det andra argumentet använde vi LEN-funktionen för att beräkna antalet värdeelement lagrade i funktionen "@CityNames" och slutligen subtraherade vi 1 från den. Detta tar bort det avslutande kommatecken från strängen. Utdatan kommer att se ut så här:
[Leeds],[Liverpool],[London],[Manchester]
Konvertera SQL-fråga till sträng
Nu kan vi förhoppningsvis använda variabeln "@CityNames" i vår pivotoperator så här:
PIVOT( AVG(total_score) FOR city IN ( @CityNames )
Vi kan dock inte använda en variabel i vår pivotoperator. Det alternativa tillvägagångssättet är att konvertera vår kompletta SQL-fråga till en sträng. Inuti den här strängen kopplar vi in vår "@CityNames"-variabel.
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' DECLARE @Query NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) SET @Query = 'SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN (' + @CityNames +') ) AS StudentPivotTable' PRINT @Query
Här deklarerade vi en variabel "@Query" och lagrade vår SQL-fråga i denna variabel. Inuti pivotoperatorn sammanfogade vi värdet som lagrats i variabeln "@CityNames". För att se hur den körda frågan ser ut har vi skrivit ut värdet på variabeln "@Query". Den resulterande frågan kommer att se ut så här i utdata:
SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN ([Leeds],[Liverpool],[London],[Manchester]) ) AS StudentPivotTable
Det är exakt den typ av fråga vi vill köra. Detta är dock i String-format. Det sista steget är att köra denna SQL-fråga lagrad som en textsträng. För att göra detta kommer vi att använda Dynamic SQL.
Köra dynamisk SQL
Vi använder den inbyggda proceduren "sp_executesql" för att köra dynamisk SQL. Vi kommer att använda denna lagrade procedur för att exekvera frågan lagrad i @Query-variabeln. Vår sista fråga som skapar en dynamisk pivottabell ser ut så här:
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' DECLARE @Query NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) SET @Query = 'SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN (' + @CityNames +') ) AS StudentPivotTable' EXECUTE sp_executesql @Query
När du kör ovanstående fråga bör du se följande resultat:
[tabell id=37 /]
Men den här gången angav vi inte manuellt värdena för rubrikerna i pivottabellen. Istället har rubrikerna beräknats dynamiskt vilket resulterat i en dynamisk pivottabell.