You are on page 1of 7

Rollingupmultiplerowsintoasinglerowandcolumn

forSQLServerdata
By:DouglasP.Castilho|ReadComments(63)|RelatedTips:More>TSQL

Problem
Ineedawaytorollupmultiplerowsintoonerowandonecolumn.IknowIcanrollupmultiplerowsintoonerow
usingPivot,butIneedallofthedataconcatenatedintoasinglecolumninasinglerow.Inthistipwelookata
simpleapproachtoaccomplishthis.

Solution
Toillustratewhatisneeded,hereisasampleofdatainatable:

Thisisoneexampleofwhatwewanttheendresulttolooklike:

ThesolutionproposedinthistipexplorestwoSQLServercommandsthatcanhelpusachievetheexpectedresults.
ThecommandstobeusedareSTUFFandFORXML.

PreparingSampleData
Beforewebegin,we'llcreatesometablesandsampledatawhichthefollowingscriptwilldoforus.

CREATETABLESALES_SECTORS(
SEC_IDINT,
SEC_NAMEVARCHAR(30))
GO
CREATETABLEUSRS(
USR_IDINT,
USR_NAMEVARCHAR(30),
SEC_IDINT
)
GO
CREATETABLEADV_CAMPAIGN(
ADV_IDINT,
ADV_NAMEVARCHAR(30)
)
GO
CREATETABLEUSR_ADV_CAMPAIGN(
USR_IDINT,
ADV_IDINT
)
GO
CREATETABLESEC_ADV_CAMPAIGN(
SEC_IDINT,
ADV_IDINT
)
GO
INSERTINTOSALES_SECTORS(SEC_ID,SEC_NAME)VALUES(1,'ENTERTAINMENT')
INSERTINTOSALES_SECTORS(SEC_ID,SEC_NAME)VALUES(2,'CLOTHES')
GO
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(1,'ANDERSON',1)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(2,'CHARLES',1)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(3,'DANNY',1)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(4,'LUCAS',1)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(5,'KEITH',2)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(6,'STEFAN',2)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(7,'EDUARD',2)
INSERTINTOUSRS(USR_ID,USR_NAME,SEC_ID)VALUES(8,'BRAD',2)
GO
INSERTINTOADV_CAMPAIGN(ADV_ID,ADV_NAME)VALUES(1,'SONYENTERTAINMENT')
INSERTINTOADV_CAMPAIGN(ADV_ID,ADV_NAME)VALUES(2,'BEATSSOUNDS')
INSERTINTOADV_CAMPAIGN(ADV_ID,ADV_NAME)VALUES(3,'BOOSE')
INSERTINTOADV_CAMPAIGN(ADV_ID,ADV_NAME)VALUES(4,'POLORALPHLAUREN')
INSERTINTOADV_CAMPAIGN(ADV_ID,ADV_NAME)VALUES(5,'LACOSTE')
GO
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(1,1)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(1,2)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(2,2)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(2,3)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(3,3)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(4,2)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(5,4)

INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(6,5)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(7,4)
INSERTINTOUSR_ADV_CAMPAIGN(USR_ID,ADV_ID)VALUES(8,5)
GO
INSERTINTOSEC_ADV_CAMPAIGN(SEC_ID,ADV_ID)VALUES(1,1)
INSERTINTOSEC_ADV_CAMPAIGN(SEC_ID,ADV_ID)VALUES(1,2)
INSERTINTOSEC_ADV_CAMPAIGN(SEC_ID,ADV_ID)VALUES(1,3)
INSERTINTOSEC_ADV_CAMPAIGN(SEC_ID,ADV_ID)VALUES(2,4)
INSERTINTOSEC_ADV_CAMPAIGN(SEC_ID,ADV_ID)VALUES(2,5)
GO

STUFF()Function
Beforegoingtotheexamples,weneedtounderstandtheworkingsofthecommandsmentionedabove.The
STUFF()functionputsastringinanotherstring,fromaninitialposition.Withthiswecaninsert,replaceorremove
oneormorecharacters.
ThissyntaxisSTUFF(character_expression,start,length,replaceWith_expression):
character_expression:stringtobemanipulated
start:initialpositiontostart
length:numberofcharacterstobemanipulated
replaceWith_expression:characterstobeused
HereisanexampleofthehowtousetheSTUFFcommand.
Forourexamplewehaveastringthatlookslikethis:
KEITHSTEFANEDUARDBRAD

Wewanttoremovethefirstfromthelistsoweendupwiththisoutput:
KEITHSTEFANEDUARDBRAD

TodothiswecanusetheSTUFFcommandasfollowstoreplacethefirstinthestringwithanemptystring.
SELECTSTUFF('KEITHSTEFANEDUARDBRAD',1,1,'')

Andthisreturnsthisoutput:
KEITHSTEFANEDUARDBRAD

FORXMLClause
TheFORXMLclause,willreturntheresultsofaSQLqueryasXML.TheFORXMLhasfourmodeswhichareRAW,
AUTO,EXPLICITorPATH.WewillusethePATHoption,whichgeneratessingleelementsforeachrowreturned.

Ifweusearegularquerysuchasthefollowingitwillreturntheresultsetshownbelow.
SELECT
SS.SEC_NAME,
US.USR_NAME
FROMSALES_SECTORSSS
INNERJOINUSRSUSONUS.SEC_ID=SS.SEC_ID
ORDERBY1,2

Ifwetakethisastepfurther,wecanusetheFORXMLPATHoptiontoreturntheresultsasanXMLstringwhichwill
putallofthedataintoonerowandonecolumn.
SELECT
SS.SEC_NAME,
US.USR_NAME
FROMSALES_SECTORSSS
INNERJOINUSRSUSONUS.SEC_ID=SS.SEC_ID
ORDERBY1,2
FORXMLPATH('')

PuttingItAllTogether
Example1
Nowthatweseewhateachofthesecommandsdoeswecanputthesetogethertogetourfinalresult.
TheexamplequerybelowusesasubquerywherewearereturningXMLdatafortheUSR_NAMEfromtableUSRS
andjoiningthistotheouterquerybySEC_IDfromtableSALES_SECTORS.Foreachvaluefromtheinnerquery
weareconcatenatinga""andthentheactualvaluetohaveallofthedatafromallrowsconcatenatedintoone
column.WearegroupingbySEC_NAMEtoshowallUSERSwithinthatSECTOR.

SELECT
SS.SEC_NAME,
(SELECT';'+US.USR_NAME
FROMUSRSUS
WHEREUS.SEC_ID=SS.SEC_ID
FORXMLPATH(''))[SECTORS/USERS]
FROMSALES_SECTORSSS
GROUPBYSS.SEC_ID,SS.SEC_NAME
ORDERBY1

Thebelowistheoutputforthisquery.WecanseethatwehavetheleadingintheSECTORS/USERScolumn
whichwedon'twant.

Inthismodifiedexample,wearenowusingtheSTUFFfunctiontoremovetheleadinginthestring.
SELECT
SS.SEC_NAME,
STUFF((SELECT';'+US.USR_NAME
FROMUSRSUS
WHEREUS.SEC_ID=SS.SEC_ID
FORXMLPATH('')),1,1,'')[SECTORS/USERS]
FROMSALES_SECTORSSS
GROUPBYSS.SEC_ID,SS.SEC_NAME
ORDERBY1

Andwegetthisresultset:

IfwealsowanttoordertheSECTORS/USERSdatawecanmodifythequeryasfollows:
SELECT
SS.SEC_NAME,
STUFF((SELECT';'+US.USR_NAME
FROMUSRSUS
WHEREUS.SEC_ID=SS.SEC_ID
ORDERBYUSR_NAME
FORXMLPATH('')),1,1,'')[SECTORS/USERS]

FROMSALES_SECTORSSS
GROUPBYSS.SEC_ID,SS.SEC_NAME
ORDERBY1

Example2
Ifwewantthisalltobeinonecolumnwecanchangethequeryalittleasfollows:
SELECT
SS.SEC_NAME+':'+
STUFF((SELECT';'+US.USR_NAME
FROMUSRSUS
WHEREUS.SEC_ID=SS.SEC_ID
FORXMLPATH('')),1,1,'')[SECTORS/USERS]
FROMSALES_SECTORSSS
GROUPBYSS.SEC_ID,SS.SEC_NAME
ORDERBY1

Andthisgivesusthisresult:

Example3
ThisexampletakesitastepfurtherwherewehavemultiplesubqueriestogiveusdatabasedonUSERSwithin
CAMPAIGNSwithinSECTORS.
SELECT
SS.SEC_ID,
SS.SEC_NAME,
STUFF((SELECT';'+AC.ADV_NAME+'('+
STUFF((SELECT','+US.USR_NAME
FROMUSR_ADV_CAMPAIGNUAC
INNERJOINUSRSUS
ONUS.USR_ID=UAC.USR_ID
WHEREUAC.ADV_ID=SAC.ADV_ID
FORXMLPATH('')),1,1,'')+')'
FROMADV_CAMPAIGNAC

INNERJOINSEC_ADV_CAMPAIGNSAC
ONSAC.ADV_ID=AC.ADV_IDANDSAC.SEC_ID=SS.SEC_ID
ORDERBYAC.ADV_NAME
FORXMLPATH('')),1,1,'')[CAMPAIGNS/USERSPERSECTOR]
FROMSALES_SECTORSSS
GROUPBY
SS.SEC_ID,
SS.SEC_NAME

Conclusion
TherearealwaysseveraloptionstocompleteataskwithinSQLServerandweshouldtakethetimetoexplorethe
capabilitiesofferedbythedatabasebeforedevelopinglargeandcomplexcode.Ihopethisisonemoreofthose
examplesthatshowstherearesometimeseasierapproachesthanyouthinkmightbeavailable.

NextSteps
Takethisfurtherandcreatesimplequeriesandthendeepenthecomplexityofthecode.
Explorethecommandsusedinthistipfurthertoseewhatotherthingsyoumightbeabletodo.
SomemoredetailsaboutthecommandsusedabovecanbeobtainedfromMSDNusingthelinksbelow:
STUFF(TransactSQL)
FORXML(SQLSERVER)

LastUpdate:4/5/2013

Abouttheauthor
DouglasCastilhohasbeenaSQLServerDBAover6years,focusesontuning,backup,disasterrecovery,mirroring,TSQL,PL
SQLand.NET
Viewallmytips

RelatedResources

MoreDatabaseDeveloperTips...

You might also like