Sunteți pe pagina 1din 10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

Articles Database Database SQL Server

Dynamic Pivoting in SQL Server


By Scott Clewell, 14 Jul 2008
3.90 (6 votes)

Download source code - 1.72 KB

Introduction
Pivoting data is sometimes necessary to create BI data sets. It can be easy to do, and it also can be a hard task to complete, especially if you do not know how wide your data is going to be when it pivots out. Using T-SQL, you can dynamically create the pivot queries instead of spending hours trying to find the values that need to be pivoted, or writing out a long aggregate query.

The code
I have two examples: One pivots data from system tables and counts the number of columns it has with a certain data type. The second one finds the average number of days between dates on a table that is used for activity tracking. I'll break down the code and explain what is going on. If you are not familiar with the pivot and unpivot syntaxes, read up on those before going on.

First example
In this example, we are using the following system tables: sys.columns, sys.objects, and sys.types. We are going to find the number of columns that the tables have with the data types which they use. We could hard-code all of the data-types, then have a bunch of unused columns with 0 in them for every table, or we could dynamically make a list of the data-types that are used, then pivot from there. First, we'll select the data into a global temporary table (##Tables).
i ojc_d'epb.#als)i ntnl f beti(tmd.#Tbe' s o ul do tbe#Tbe rp al #als slc eet cNm A ClmNm .ae s ounae ,.aeA DtTp tNm s aaye ,.aeA Tbeae oNm s alNm It #Tbe no #als fo ssclmsc rm y.oun ji ssojcsoo on y.bet n oojc_d=cojc_d .beti .beti
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 1/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

Adotp_ec='SRTBE n .yeds UE_AL' ji sstpsto on y.ye n tsse_yei =csse_yei .ytmtp_d .ytmtp_d

What we have in ##Tables is a list of all of the columns with their data type and parent table. Next, a cursor is used and goes through the distinct data-types that we have in ##Table. We add each data-type that is in the table into @ t q , which will be used later. DSl
Dcae@q vrhrmx elr sl aca(a) ,DSlvrhrmx @tq aca(a) ,DtTp vrhr10 @aaye aca(0) ,cbt @ i St@tq =' e DSl ' St@ =0 e c Dcaedt_yecro elr aatp usr frSlc Dsic DtTp o eet itnt aaye Fo #Tbe rm #als OdrB 1 re y Oe dt_ye pn aatp fthnx fo dt_ye ec et rm aatp it @aaye no DtTp Wie@FthSau =0 hl @ec_tts Bgn ei i(c=1 f@ ) St@tq =@tq +'[ +@aaye+'' e dSl DSl ,' DtTp ] es le Bgn ei St@tq =@tq +''+@aaye+'' e DSl DSl [ DtTp ] St@ =1 e c Ed n Fthnx fo dt_ye ec et rm aatp it @aaye no DtTp

Ed n

coedt_ye ls aatp dalct dt_ye eloae aatp

Finally, we write out @ q with the pivot syntax, and pivot on the list of values in @ t q . We execute Sl DSl @ q and get the results. Sl
St@q ='eetTbeae '+@tq e Sl Slc alNm, DSl St@q =@q +' e Sl Sl Fo rm (eetClmNm,DtTp,TbeaeFo #Tbe)p Slc ounae aaye alNm rm #als Pvt io ( CutClmNm) on(ounae FrDtTp i ( +@tq +') o aaye n ' DSl )a pt s v' pit@q rn sl ee(Sl xc@q)

The final results will be similar to the table below: TableName Bit Varchar Int DateTime Dates 0 10 4 2
2/10

www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

Test

Example 2
The scenario for this next example is: You have a table that tracks activities when accounts log on. You are asked to find the average number of days between logins for the accounts (and you ignorantly nod your head, not knowing the mess you are about to get into). The problem: There is no way to hard code the values to pivot on, making the pivot syntax difficult, if not unusable. The complexity of finding the average number of days between activities can sound easy, not realizing that your data is stored vertically, in this situation, and you need to have it pivot horizontally, then unpivot once you have the days between activities. Sounds confusing, eh? Here is a small break down of what the tables will be doing: They will start out vertically, with one activity per row: ID Date 1 1 1 2 2 2 2 2 20080701 20080705 20080710 20080615 20080627 20080702 20080711 20080714

Next, the activities will be laid out horizontally: ID 5 1 2 4 3 2 1

UL 20080701 20080705 20080710 N L

NL UL

20080615 20080627 20080702 20080711 20080714

After laying the table out horizontally, we will use the D t D f function to find the difference in days: aeif ID DateDiff5 DateDiff4 DateDiff3 DateDiff2 1 2 4 12 5 5 0 9 0 3

Finally, we unpivot and get the averages. Now, let's get into the code. For this example, we're going to be using a small data set. The following code will create a global temporary table and populate it:
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 3/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

-tm i tetbew'egigt ue -ep s h al er on o s i ojc_d'epb.#ep)i ntnl f beti(tmd.#Tm' s o ul do tbe#Tm rp al #ep Cet Tbe#Tm rae al #ep ( I it d n ,aedttm Dt aeie ) -Isr ts vle -net et aus Isr It #Tm net no #ep slc 1 '0871 eet , 2000' uinalslc 1 '0875 no l eet , 2000' uinalslc 1 '0870 no l eet , 2001' uinalslc 2 '0865 no l eet , 2001' uinalslc 2 '0867 no l eet , 2002' uinalslc 2 '0872 no l eet , 2000' uinalslc 2 '0871 no l eet , 2001' uinalslc 2 '0874 no l eet , 2001'

Next, we define the parameters that will be used, then find the maximum number of activities that happened for a single ID. This number is how wide the table will be. We find that number by using a simple subquery.
-Dcaevrals -elr aibe Dcae@q vrhrmx elr Sl aca(a) ,Cutrit @one n ,Mxit @a n -Stcutrt tems atvt -e one o h ot ciiy St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) St@a =@one e Mx Cutr

After we get the maximum width, the table will be created. A w i e h l loop is used to create the columns with the counter number as the column name. The SQL statement is dynamically created, then executed.
-Cet atbewt tewdho tems atvt -rae al ih h it f h ot ciiy St@q ='raeTbe#Atvt (i it' e Sl Cet al #ciiy d n Wie@one >0 hl Cutr Bgn ei St@q =@q +'[ +cs(cutra vrhr +' dttm' e Sl Sl ,' at@one s aca) ] aeie St@one =@one -1 e Cutr Cutr Ed n St@q =@q +'' e Sl Sl ) -Mk sr tbedenteit -ae ue al os' xs i ojc_d'epb.#ciiy)i ntnl f beti(tmd.#Atvt' s o ul Do Tbe#Atvt rp al #ciiy Ee (sl xc @q)

Once the table is created, it will now get populated by the following code. The code will look into the table with the data (##temp), and get the minimum date that has not been inserted yet. It will insert all values for the IDs at once, which is done by sub-querying. The queries are executed in the w i e h l loop. After this code runs, the ##Activity table will look like the second one in the breakdown. Notice that we reset the counter back to the maximum width. This will be done before any step that loops.
-Isr i' it tetbeta wscetd -net ds no h al ht a rae Isr It #Atvt (d net no #ciiy i) Slc dsic I Fo #Tm eet itnt d rm #ep
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 4/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

-RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Lo truhadisr dts -op hog n net ae Wie@one >0 hl Cutr Bgn ei I (Cutr=@a) f @one Mx Bgn ei St@q ='pae#Atvt St[ +Cs(Cutra vrhr+' ' e Sl Udt #ciiy e ' at@one s aca) ] St@q =@q +'Frtciiyfo #Atvt a' e Sl Sl =isAtvt rm #ciiy St@q =@q +'etJi (eetaI,mnDt)a FrtciiyFo e Sl Sl Lf on Slc .d i(ae s isAtvt rm #Atvt a' #ciiy St@q =@q +'etJi #Tm to aI =tI ' e Sl Sl Lf on #ep n .d .d St@q =@q +'ru B ai)to ai =tI' e Sl Sl Gop y .d n .d .d Ee(Sl xc@q) St@one =@one -1 e Cutr Cutr

Ed n Es le Bgn ei St@q ='pae#Atvt St[ +Cs(Cutra vrhr +' ' e Sl Udt #ciiy e ' at@one s aca) ] St@q =@q +' Atvt fo #Atvt a' e Sl Sl = ciiy rm #ciiy St@q =@q +'etJi (eetaI,MnDt)a Atvt ' e Sl Sl Lf on Slc .d i(ae s ciiy St@q =@q +'rm#Atvt aLf Ji #Tm to ' e Sl Sl Fo #ciiy et on #ep n St@q =@q +'.d=ti weeDt >' e Sl Sl ai .d hr ae St@q =@q +''+Cs(Cutr+1a vrhr +' ' e Sl Sl [ at@one s aca) ] St@q =@q +'ru b ai)to ai =ti' e Sl Sl gop y .d n .d .d Ee(Sl xc@q) St@one =@one -1 e Cutr Cutr

Ed n

Ed n

Next, we use the D t D f function to find the difference between days in the horizontal table. The aeif C a e c function is used if the date is n l (indicating that the width is shorter for that ID) and sets the olse ul difference to 0. Coalescing the n l s to 0 will be accounted for when we do the final average. As usual, a ul loop is used to loop through all of the columns that have been dynamically created. Since the I s r net I t syntax is being used, there will be another table created. no
-RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Cet Tbewt dy btenatvt -rae al ih as ewe ciiy St@q ='eeti ' e Sl Slc d Wie@one >1 hl Cutr Bgn ei St@q =@q +'Caec(aeifd, e Sl Sl ,olseDtDf(d' St@q =@q +''+Cs(Cutra vrhr +',' e Sl Sl [ at@one s aca) ] St@q =@q +''+Cs(Cutr-1a vrhr +')0 ' e Sl Sl [ at@one s aca) ],) St@q =@q +'s[aeif +Cs(Cutra vrhr +'' e Sl Sl A DtDf' at@one s aca) ] Ed n St@one =@one -1 e Cutr Cutr

St@q =@q +'it #Aeae fo #Atvt' e Sl Sl no #vrgs rm #ciiy i ojc_d'epb.#vrgs)i ntnl f beti(tmd.#Aeae' s o ul do tbe#Aeae rp al #vrgs Ee(sl xc@q)

The final part to this puzzle is to unpivot the data. Since we have a table that is in the right format, we can use the unpivot syntax. w i e h l loops are used twice to go through the columns: one time for the upper
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 5/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

unpivot syntax, and one time for the lower unpivot syntax. The unpivot query is a sub-query of the Avg query. In the query that gets the average, a w e e h r clause is used to average only the columns that have a value greater than 0.
-RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Upvt -nio St@q ='eeti,AgCs(vsa fot) e Sl Slc d v(atag s la)' St@q =@q +'fo (eeti,DfDy,vs' e sl Sl rm slc d ifasag St@q =@q +'fo (eeti' e Sl Sl rm slc d wie@one >1 hl Cutr Bgn ei St@q =@q +'[aeif +Cs(Cutra vrhr +'' e Sl Sl ,dtdf' at@one s aca) ] St@one =@one -1 e Cutr cutr Ed n -RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) St@q =@q +'fo #aeae)aupvt(vsfrdfdy i ( e Sl Sl rm #vrgs nio ag o ifas n ' Wie@one >1 hl Cutr Bgn ei i(cutr=@a) f@one Mx Bgn ei St@q =@q +'dtdf'+Cs(cutra vrhr +'' e Sl Sl [aeif at@one s aca) ] St@one =@one -1 e Cutr Cutr Ed n Es le Bgn ei St@q =@q +'[aeif +cs(Cutra vrhr +'' e Sl Sl ,dtdf' at@one s aca) ] St@one =@one -1 e Cutr Cutr Ed n Ed n St@q =@q +')a upt fweeag >0gopb i' e Sl Sl ) s nv) hr vs ru y d Ee (Sl xc @q)

After all of that has executed, the results will turn out to be: ID AverageDays 1 2 4.5 7.5

If you haven't noticed yet, we haven't used the pivot syntax. Why is that? Since we were building the table horizontally as we went on, we didn't need to use the pivot statement. Also, the pivot syntax wouldn't have helped much because we would have ended up doing the same steps with the population and the finding the difference in days. The unpivot statement was used because we have a table that was in a good format for that statement.

Ending notes
The evil software vendors don't always make data sets that can be used for quick and easy BI analysis that your evil boss wants.
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 6/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

Thanks for reading this far (I know it's quite a big article). Good luck!

The code as a whole


Sample 1:
i ojc_d'epb.#als)i ntnl f beti(tmd.#Tbe' s o ul do tbe#Tbe rp al #als slc eet cNm A ClmNm .ae s ounae ,.aeA DtTp tNm s aaye ,.aeA Tbeae oNm s alNm It #Tbe no #als fo ssclmsc rm y.oun ji ssojcsoo on y.bet n oojc_d=cojc_d .beti .beti Adotp_ec='SRTBE n .yeds UE_AL' ji sstpsto on y.ye n tsse_yei =csse_yei .ytmtp_d .ytmtp_d Dcae@q vrhrmx elr sl aca(a) ,DSlvrhrmx @tq aca(a) ,DtTp vrhr10 @aaye aca(0) ,cbt @ i St@tq =' e DSl ' St@ =0 e c Dcaedt_yecro elr aatp usr frSlc Dsic DtTp o eet itnt aaye Fo #Tbe rm #als OdrB 1 re y Oe dt_ye pn aatp fthnx fo dt_ye ec et rm aatp it @aaye no DtTp Wie@FthSau =0 hl @ec_tts Bgn ei i(c=1 f@ ) St@tq =@tq +'[ +@aaye+'' e dSl DSl ,' DtTp ] es le Bgn ei St@tq =@tq +''+@aaye+'' e DSl DSl [ DtTp ] St@ =1 e c Ed n Fthnx fo dt_ye ec et rm aatp it @aaye no DtTp

Ed n

coedt_ye ls aatp dalct dt_ye eloae aatp pit@tq rn DSl St@q ='eetTbeae '+@tq e Sl Slc alNm, DSl St@q =@q +' e Sl Sl Fo rm (eetClmNm,DtTp,TbeaeFo #Tbe)p Slc ounae aaye alNm rm #als Pvt io ( CutClmNm) on(ounae FrDtTp i ( +@tq +') o aaye n ' DSl )a pt s v'
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 7/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

pit@q rn sl ee(Sl xc@q)

Sample 2
-tm i tetbew'egigt ue -ep s h al er on o s i ojc_d'epb.#ep)i ntnl f beti(tmd.#Tm' s o ul do tbe#Tm rp al #ep Cet Tbe#Tm rae al #ep ( I it d n ,aedttm Dt aeie ) -Isr ts vle -net et aus Isr It #Tm net no #ep slc 1 '0871 eet , 2000' uinalslc 1 '0875 no l eet , 2000' uinalslc 1 '0870 no l eet , 2001' uinalslc 2 '0865 no l eet , 2001' uinalslc 2 '0867 no l eet , 2002' uinalslc 2 '0872 no l eet , 2000' uinalslc 2 '0871 no l eet , 2001' uinalslc 2 '0874 no l eet , 2001' -Dcaevrals -elr aibe Dcae@q vrhrmx elr Sl aca(a) ,Cutrit @one n ,Mxit @a n -Stcutrt tems atvt -e one o h ot ciiy St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) St@a =@one e Mx Cutr -Cet atbewt tewdho tems atvt -rae al ih h it f h ot ciiy St@q ='raeTbe#Atvt (i it' e Sl Cet al #ciiy d n Wie@one >0 hl Cutr Bgn ei St@q =@q +'[ +cs(cutra vrhr +' dttm' e Sl Sl ,' at@one s aca) ] aeie St@one =@one -1 e Cutr Cutr Ed n St@q =@q +'' e Sl Sl ) -Mk sr tbedenteit -ae ue al os' xs i ojc_d'epb.#ciiy)i ntnl f beti(tmd.#Atvt' s o ul Do Tbe#Atvt rp al #ciiy Ee (sl xc @q) -Isr i' it tetbeta wscetd -net ds no h al ht a rae Isr It #Atvt (d net no #ciiy i) Slc dsic I Fo #Tm eet itnt d rm #ep -RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Lo truhadisr dts -op hog n net ae Wie@one >0 hl Cutr Bgn ei I (Cutr=@a) f @one Mx Bgn ei St@q ='pae#Atvt St[ +Cs(Cutra vrhr+' ' e Sl Udt #ciiy e ' at@one s aca) ] St@q =@q +'Frtciiyfo #Atvt a' e Sl Sl =isAtvt rm #ciiy
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 8/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

St@q =@q +'etJi (eetaI,mnDt)a FrtciiyFo e Sl Sl Lf on Slc .d i(ae s isAtvt rm #Atvt a' #ciiy St@q =@q +'etJi #Tm to aI =tI ' e Sl Sl Lf on #ep n .d .d St@q =@q +'ru B ai)to ai =tI' e Sl Sl Gop y .d n .d .d Ee(Sl xc@q) St@one =@one -1 e Cutr Cutr

Ed n Es le Bgn ei St@q ='pae#Atvt St[ +Cs(Cutra vrhr +' ' e Sl Udt #ciiy e ' at@one s aca) ] St@q =@q +' Atvt fo #Atvt a' e Sl Sl = ciiy rm #ciiy St@q =@q +'etJi (eetaI,MnDt)a Atvt ' e Sl Sl Lf on Slc .d i(ae s ciiy St@q =@q +'rm#Atvt aLf Ji #Tm to ' e Sl Sl Fo #ciiy et on #ep n St@q =@q +'.d=ti weeDt >' e Sl Sl ai .d hr ae St@q =@q +''+Cs(Cutr+1a vrhr +' ' e Sl Sl [ at@one s aca) ] St@q =@q +'ru b ai)to ai =ti' e Sl Sl gop y .d n .d .d Ee(Sl xc@q) St@one =@one -1 e Cutr Cutr

Ed n

Ed n

-RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Cet Tbewt dy btenatvt -rae al ih as ewe ciiy St@q ='eeti ' e Sl Slc d Wie@one >1 hl Cutr Bgn ei St@q =@q +'Caec(aeifd, e Sl Sl ,olseDtDf(d' St@q =@q +''+Cs(Cutra vrhr +',' e Sl Sl [ at@one s aca) ] St@q =@q +''+Cs(Cutr-1a vrhr +')0 ' e Sl Sl [ at@one s aca) ],) St@q =@q +'s[aeif +Cs(Cutra vrhr +'' e Sl Sl A DtDf' at@one s aca) ] Ed n St@one =@one -1 e Cutr Cutr

St@q =@q +'it #Aeae fo #Atvt' e Sl Sl no #vrgs rm #ciiy i ojc_d'epb.#vrgs)i ntnl f beti(tmd.#Aeae' s o ul do tbe#Aeae rp al #vrgs Ee(sl xc@q) -RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) -Upvt -nio St@q ='eeti,AgCs(vsa fot) e Sl Slc d v(atag s la)' St@q =@q +'fo (eeti,DfDy,vs' e sl Sl rm slc d ifasag St@q =@q +'fo (eeti' e Sl Sl rm slc d wie@one >1 hl Cutr Bgn ei St@q =@q +'[aeif +Cs(Cutra vrhr +'' e Sl Sl ,dtdf' at@one s aca) ] St@one =@one -1 e Cutr cutr Ed n -RstCutr -ee one St@one =(eetMxct e Cutr Slc a(n) Fo (eetCut* a ctFo #tm gopb I)t rm Slc on() s n rm #ep ru y d ) St@q =@q +'fo #aeae)aupvt(vsfrdfdy i ( e Sl Sl rm #vrgs nio ag o ifas n ' Wie@one >1 hl Cutr Bgn ei i(cutr=@a) f@one Mx
www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print 9/10

06/02/13

Dynamic Pivoting in SQL Server - CodeProject

Ed n

Bgn ei St@q =@q +'dtdf'+Cs(cutra vrhr +'' e Sl Sl [aeif at@one s aca) ] St@one =@one -1 e Cutr Cutr Ed n Es le Bgn ei St@q =@q +'[aeif +cs(Cutra vrhr +'' e Sl Sl ,dtdf' at@one s aca) ] St@one =@one -1 e Cutr Cutr Ed n

St@q =@q +')a upt fweeag >0gopb i' e Sl Sl ) s nv) hr vs ru y d Ee (Sl xc @q)

History
7/14/2008 - Published article.

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Scott Clewell
United States Member

Scott is attending Washburn University studying in Computer Science. He works as a DBA with CommunityAmerica Credit Union and develops [Windows] applications independently.

Comments and Discussions


2 messages have been posted for this article Visit http://www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server to post and view comments on this article, or click here to get a print view with messages.
Permalink | Advertise | Privacy | Mobile Web01 | 2.6.130204.1 | Last Updated 14 Jul 2008 Article Copyright 2008 by Scott Clewell Everything else Copyright CodeProject, 1999-2013 Terms of Use

www.codeproject.com/Articles/27776/Dynamic-Pivoting-in-SQL-Server?display=Print

10/10

S-ar putea să vă placă și