Tags
Versie SQL Server 2005 en hoger
Het gebeurt in een database regelmatig dat waarden moeten worden samengevoegd uit verschillende rijen. Bijvoorbeeld bij het maken van een kommagescheiden lijst van alle landen in een database. Tot voor kort gebruikte ik daar altijd COALESCE voor. Deze functie retourneert de eerste niet lege waarde van een reeks argumenten en COALESCE kan recursief in een query worden gebruikt als ook de mogelijkheid bestaat om variabelen in te zetten (maar dat is niet altijd mogelijk).
Voorbeeldtabel:
Ik heb een tabel met alle Europese landen en een boolean waarde die aangeeft of ze momenteel (mei 2012) lid zijn van de Europese Unie.
| Land | Lid |
| België | 1 |
| Bulgarije | 1 |
| Cyprus | 1 |
| Tsjechië | 1 |
| Zweden | 1 |
| Verenigd Koninkrijk | 1 |
| Kroatië | 0 |
| Albanië | 0 |
| Andorra | 0 |
| Macedonië | 0 |
| IJsland | 0 |
| Montenegro | 0 |
| Servië | 0 |
| Turkije | 0 |
| … |
Om nu met Coalesce een lijst van alle landen te maken die lid zijn gebruik:
DECLARE @EuroUnie NVARCHAR(MAX) SELECT @EuroUnie = COALESCE(@EuroUnie + ', ', '') + Land FROM tblLanden WHERE lid = 1 order by Land
De uitkomst is:
België, Bulgarije, Cyprus, Denemarken, Duitsland, Estland, Finland, Frankrijk, Griekenland, Hongarije, Ierland, Italië, Letland, Litouwen, Luxemburg, Malta, Nederland, Oostenrijk, Polen, Portugal, Roemenie, Slovenië, Slowakije, Tsjechië, Verenigd Koninkrijk, Zweden
Ooit las ik ergens dat dit ook kan met XML, maar ik heb er eigenlijk nooit echt naar gekeken. Waarom XML inzetten als je geen XML nodig hebt? Tot ik op een dag een query nodig had die geen variabele kon gebruiken. Ik zocht de XML-variant op en tot mijn grote vreugde is die velen malen sneller. 23.000 records samenvoegen in een fractie van de tijd die COALESCE nodig heeft.
De code is wel wat lastiger te lezen, dus hierbij een uitleg stap voor stap.
Eerst de basisquery. Deze heeft weinig uitleg nodig.
select Land from tblLanden where lid=1 order by Land
Deze kan in XML worden gevangen door een for xml path toe te voegen
select Land from tblLanden where lid=1 order by Land for xml path('')
Doordat ik geen parameters specificeer bij path, zal SQL Server de naam van het veld gebruiken als XML naam:
<Land>België</Land><Land>Bulgarije</Land><Land>Cyprus</Land> <Land>Denemarken</Land><Land>Duitsland</Land><Land>Estland</Land> <Land>Finland</Land><Land>Frankrijk</Land><Land>Griekenland</Land> <Land>Hongarije</Land><Land>Ierland</Land><Land>Italië</Land> <Land>Letland</Land><Land>Litouwen</Land><Land>Luxemburg</Land> <Land>Malta</Land><Land>Nederland</Land><Land>Oostenrijk</Land> <Land>Polen</Land><Land>Portugal</Land><Land>Roemenië</Land> <Land>Slovenië</Land><Land>Slowakije</Land><Land>Tsjechië</Land> <Land>Verenigd Koninkrijk</Land><Land>Zweden</Land>
Maar als we het veld nu specifiek naar text() hernoemen, zal SQL server de xml names weglaten.
select Land as [text()] from tblLanden where lid=1 order by Land for xml path('') BelgiëBulgarijeCyprusDenemarkenDuitslandEstlandFinlandFrankrijk GriekenlandHongarijeIerlandItaliëLetlandLitouwenLuxemburgMalta NederlandOostenrijkPolenPortugalRoemeniëSloveniëSlowakijeTsjechië Verenigd KoninkrijkZweden
Even wat spaties en komma’s toevoegen en we zijn er bijna.
select ', ' + Land as [text()] from tblLanden where lid=1 order by Land for xml path('') , België, Bulgarije, Cyprus, Denemarken, Duitsland, Estland, Finland, Frankrijk, Griekenland, Hongarije, Ierland, Italië, Letland, Litouwen, Luxemburg, Malta, Nederland, Oostenrijk, Polen, Portugal, Roemenië, Slovenië, Slowakije, Tsjechië, Verenigd Koninkrijk, Zweden
De komma die ontstaat voor het eerste land, is niet moeilijk weg te werken. Eerst ga ik een ander issue oplossen. For XML geeft een XML datatype terug in plaats van een platte string. Dit kan opgelost worden met de functie Stuff. Deze functie plaatst een string in een andere string op een bepaalde positie.
select STUFF((select ', ' + Land as [text()] from tblLanden where lid=1 order by Land for xml path('')),1,1,'')
Met Stuff kan in het XML-resultaat het eerste karakter (de overtollige komma) worden vervangen met een lege string. Het resultaat is een string.
Deze techniek heeft in mijn database met miljoenen records keer op keer laten zien dat het een stuk sneller is dan COALESCE.
Cool zeg. Dit wist ik niet, en vooral die STUFF functie is een beauty.
Ga ik zeker gebruiken.
Pingback: How-to: Waarden uit SQL server snel samenvoegen | Vijfhart Weblog