{"id":3472,"date":"2012-01-05T02:32:00","date_gmt":"2012-01-05T02:32:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/using-xml-to-pass-lists-as-parameters-in-sql-server\/"},"modified":"2017-08-03T15:26:14","modified_gmt":"2017-08-03T15:26:14","slug":"using-xml-to-pass-lists-as-parameters-in-sql-server","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/using-xml-to-pass-lists-as-parameters-in-sql-server\/","title":{"rendered":"Using XML to pass lists as parameters in SQL Server"},"content":{"rendered":"<p><i>(Updated 14th Jan 2012, \u00a0<\/i><i>26th Jan 2012, and 3rd Aug 2017)<\/i><\/p>\n<p>Every so often, before SQL Server 2016, \u00a0the question came up on forums of how to pass a list as a parameter to a SQL procedure or function.<\/p>\n<p>Nowadays, of course, one uses the STRING_SPLIT() function \u00a0 &#8230;.<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">DECLARE @tags VARCHAR(200) = 'Yan,Tyan,Tethera,Methera,Pimp,Sethera,Lethera,Hovera,Dovera,Dik,Yanadik,Tyanadik,Tethera dik,Methera dik,Bumfitt,Yanabumfit,Tyanabumfitt,Tetherabumfitt,Metherabumfitt,Giggot' \r\nSELECT value FROM STRING_SPLIT(@tags, ',')<\/pre>\n<p>There was a time that I used to love this question because one could spread so much happiness and gratitude by showing how to parse a comma-delimited list into a table. Jeff Moden has probably won the laurels for the ultimate list-parsing TSQL function, the amazing &#8216;DelimitedSplit8K&#8217;. <a href=\"http:\/\/www.sommarskog.se\/index.html\">Erland Sommarskog<\/a> has permanently nailed the topic with a very complete coverage of the various methods for using lists in SQL Server on his website.. With Table-Valued parameters, of course, the necessity for having any lists in SQL Server is enormously reduced, though it still crops up.<\/p>\n<p>Element-based XML seems, on the surface, to provide a built-in way of handling lists as parameters. No need for all those ancillary functions for splitting lists into tables, one might think. Yes, indeed, but be careful of the XQuery syntax that you use, as we&#8217;ll see.<\/p>\n<p>\u00a0Let&#8217;s just take the very simplest example of taking a list of integers and turning it into a table of integers. One can use a simple element-based XML list based on a fragment like this&#8230;<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \"> \u00a0\u00a0\u00a0\u00a0\u00a0 DECLARE @XMLlist XML\r\n \u00a0\u00a0\u00a0\u00a0\u00a0 SELECT\u00a0 @XMLList = '&lt;list&gt;&lt;i&gt;2&lt;\/i&gt;&lt;i&gt;4&lt;\/i&gt;&lt;i&gt;6&lt;\/i&gt;&lt;i&gt;8&lt;\/i&gt;&lt;i&gt;10&lt;\/i&gt;&lt;i&gt;15&lt;\/i&gt;&lt;i&gt;17&lt;\/i&gt;&lt;i&gt;21&lt;\/i&gt;&lt;\/list&gt;'\r\n <\/pre>\n<p>&#8230;to give a table like this ..<\/p>\n<pre><code>\r\nIDs\r\n-----------\r\n2\r\n4\r\n6\r\n8\r\n10\r\n15\r\n17\r\n21\r\n\u00a0\r\n(8 row(s) affected)\r\n<\/code><\/pre>\n<p>..by using this TSQL expression<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">SELECT x.y.value('.','int') AS IDs\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('\/list\/i') AS x ( y )\r\n <\/pre>\n<p>It isn&#8217;t as intuitive as a simple comma-delimited list to be sure, but it is bearable. In small doses, this shredding of element-based XML lists works fine, but,&lt;added 26th January&gt; using this particular syntax for getting the value of the element &lt;\/added 26th January&gt; one soon notices the sigh from the server as the length of the list starts to increases into four figures. Try it on a list of a hundred thousand integers and you&#8217;ll have time to eat a sandwich.<\/p>\n<p>OK. Let&#8217;s do a few measurements and do a graph.<\/p>\n<p>\u00a0<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-22930\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2012\/01\/TheFirstGraph.jpg\" alt=\"TheFirstGraph\" width=\"602\" height=\"437\" \/><\/p>\n<p>Jeff Moden&#8217;s Split function, scales beautifully as shown by the red line, and it is difficult to measure it, it is so quick. The XML eShred technique by contrast, using the element-based list, and this XQuery syntax, exhibits horrible scaling. This sort of curve is enough to strike terror into the developer. Just as a comparison, I did the process of taking a list and turning it into a table by creating a VALUES expression from the list. Even this took longer than Jeff&#8217;s function, presumably because of the overhead of compiling the expression. Of course, the comparison is rather unfair because this third approach , the &#8216;Using Values Script&#8217; approach, does no validation, but still one wonders what on earth the XML expression is DOING all that time. It must be gazing out the window, reading the paper and scratching itself, one assumes. No. Actually, the CPU is warming up on the task, so it involves frantic activity.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-22931\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2012\/01\/TheSecondGraph.jpg\" alt=\"TheSecondGraph\" width=\"601\" height=\"436\" \/><\/p>\n<p>Operations involving XML can be startlingly fast in SQL Server, but this particular critter seems startlingly slow once one gets to a three-figure number in the list that you&#8217;re turning into a relation that can then be used in SQL Expressions. Imagine this getting loose in a production system when someone starts passing more values in the list and the database suddenly slows down. How would you track the problem down?<\/p>\n<p>&lt;added 26th January&gt;The big problem here is one of the expression. As pointed out by a reader of the blog, if you modify the expression slightly to<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">SELECT x.y.value('.','int') AS IDs\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('\/list\/i\/text()') AS x ( y )\r\n <\/pre>\n<p>The shredding suddenly goes like a rocket&lt;\/added 26th January&gt;.<\/p>\n<p>There is a second, and more wordy version of the simple XML list, The attribute-based list. Here<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">\u00a0\u00a0 DECLARE @XMLlist XML\r\n\u00a0\u00a0 SELECT\u00a0\u00a0@XMLList = '&lt;list&gt;&lt;y i=\"2\" \/&gt;&lt;y i=\"4\" \/&gt;&lt;y i=\"6\" \/&gt;&lt;y i=\"8\" \/&gt;&lt;y i=\"10\" \/&gt;&lt;y i=\"15\" \/&gt;&lt;y i=\"17\" \/&gt;&lt;y i=\"21\" \/&gt;&lt;\/list&gt;'\r\n<\/pre>\n<p>&#8230;to give exactly the same table ..<\/p>\n<pre><code>\r\nIDs\r\n-----------\r\n2\r\n4\r\n6\r\n8\r\n10\r\n15\r\n17\r\n21\r\n\u00a0\r\n(8 row(s) affected)\r\n<\/code><\/pre>\n<p>by using a very similar syntax like this&#8230;<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">\u00a0\u00a0 SELECT x.y.value('@i','int')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('list\/y') AS x( y )\r\n<\/pre>\n<p>&#8216;Eh? What difference could this make?&#8217;, you&#8217;re wondering. Well, rather a lot, it turns out. Thanks to a reader comment on the first version of this blog by <b>wBob<\/b>, we can reveal that this version scales as well as Jeff&#8217;s amazing &#8216;split&#8217; function. I must admit to being rather surprised at the difference in performance of the two. By using the Attribute-based list, or the modified XQuery expression,\u00a0 you can cheerfully pass lists of substantial length to procedures without worrying, it seems. It reminded me of an argument I had some years ago with a distinguished Database Developer who was advising us all not to use XML-based lists because of their performance problems. I&#8217;d been using attribute-based\u00a0 lists simply because I copied Bob Beauchamin&#8217;s example code in &#8216;The Book&#8217;, and hadn&#8217;t hit any problem. We couldn&#8217;t convince each other. Perhaps we&#8217;d hit this performance difference.<\/p>\n<p>&lt;added 26th January&gt;We can even improve on this. as Sviridov Konstantin has pointed out, you can tweak it to the point that it goes around twice the speed of Jeff&#8217;s TSQL split function by using syntax like this&#8230;<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">\u00a0 SELECT x.y.value('.','int')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('list\/y\/@i') AS x( y )\r\n<\/pre>\n<p>Which will give a performance comparison with the the TSQL split function. These modified XML shreds must be the fastest non-CLR method of\u00a0 transferring list-based data as a variable in SQL.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-22932\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2012\/01\/FinalShootout.jpg\" alt=\"FinalShootout\" width=\"600\" height=\"436\" \/><\/p>\n<p>&lt;\/added 26th January&gt;XML in SQL Server seems to remind me of the old poem by Henry Wadsworth Longfellow. (1807-1882)<\/p>\n<p>There was a little girl, who had a little curl<br \/>\n Right in the middle of her forehead,<br \/>\n And when she was good, she was very, very good,<br \/>\n But when she was bad she was horrid.&#8217;.<\/p>\n<p>Just as a comparison, here is the horrid XML performing against the first attribute-based XML Shred, and the TSQL split function<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-22933\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2012\/01\/TheThirdGraph.jpg\" alt=\"TheThirdGraph\" width=\"602\" height=\"438\" \/><\/p>\n<p>As always, one should measure everything you can, and take nothing for granted if you have to deliver a scalable application.<\/p>\n<p>Here is the simple code I used to do the metrics. The results were simply pasted into excel and graphed.<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">--in order to record the timings, we prepare a log.\r\nDECLARE @log TABLE (\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Log_Id INT IDENTITY(1, 1),TheeVent VARCHAR( 2000 ),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0[Values] INT, CPU FLOAT DEFAULT\u00a0\u00a0@@CPU_BUSY,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DateAndTime DATETIME DEFAULT GETDATE()) ;\r\n--define working variables\r\nDECLARE @List VARCHAR(MAX),@XMLList XML,@AttributeBasedXMLList XML,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@buildScript VARCHAR(MAX), @XMLbuildScript NVARCHAR(MAX),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@ii INT, @Values INT;\r\n--and table variables to receive the results\/relations\r\nDECLARE @ByteBucket TABLE (TheNumeral INT)\r\nDECLARE @ByteBucket2 TABLE (TheNumeral INT)\r\nDECLARE @ByteBucket3 TABLE (TheNumeral INT)\r\nDECLARE @ByteBucket4 TABLE (TheNumeral INT)\r\nDECLARE @ByteBucket5 TABLE (TheNumeral INT)\r\nDECLARE @ByteBucket6 TABLE (TheNumeral INT)\r\nSET NOCOUNT ON\r\nSELECT @Values=1 --start with one item in the list\r\nWHILE @Values&lt;=3000\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0--build up the list with random integers\r\n\u00a0\u00a0SELECT @List='1',@ii=@Values\r\n\u00a0\u00a0WHILE @ii&gt;1\r\n\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0SELECT @List=@List+','+CONVERT(VARCHAR(3),CONVERT(INT,RAND()*100))\r\n\u00a0\u00a0\u00a0\u00a0SELECT @ii=@ii-1 --\u00a0\u00a0.. to the required length\r\n\u00a0\u00a0\u00a0\u00a0END\r\n\u00a0\u00a0--and pre-prepare the XML List and VALUES script\r\n\u00a0\u00a0SELECT @XMLList='&lt;list&gt;&lt;i&gt;'+REPLACE(@List,',','&lt;\/i&gt;&lt;i&gt;')+'&lt;\/i&gt;&lt;\/list&gt;'\r\n\u00a0\u00a0SELECT @BuildScript='SELECT x FROM (values ('+REPLACE(@List,',','),(')+'))d(x)'\r\n\u00a0\u00a0--and pre-prepare the XML List and VALUES script\u00a0\u00a0\r\nSELECT @XMLBuildScript='SELECT @AttributeBasedXMLList = ( SELECT x AS \"@x\" FROM (values ('\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0+REPLACE(@List,',','),(')+'))d(x) FOR XML PATH(''y''), ROOT(''root''), TYPE )'\r\nEXEC sp_executesql @XMLBuildScript, N'@AttributeBasedXMLList XML OUT',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@AttributeBasedXMLList OUT\r\n\u00a0\u00a0--try doing the delimited list function\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Using Split function',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket (TheNumeral)\r\n\u00a0\u00a0SELECT item FROM dbo.DelimitedSplit8K (@list,',')\r\n\u00a0\u00a0--use the XML Shred trick\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Element-based XML',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket2 (TheNumeral)\r\n\u00a0\u00a0SELECT x.y.value('.','int') AS IDs\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('\/list\/i') AS x ( y )\r\n\u00a0\u00a0--use the XML Shred trick\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Element-based XML (text())',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket6 (TheNumeral)\r\n\u00a0\u00a0SELECT x.y.value('.','int') AS IDs\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @XMLList.nodes('\/list\/i\/text()') AS x ( y )\r\n\u00a0\u00a0--try the VALUES method\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Using Values script',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket3 (TheNumeral)\r\n\u00a0\u00a0\u00a0\u00a0EXECUTE (@BuildScript)\r\n\u00a0\u00a0--use the XML Shred trick\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Attribute-based XML',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket4 (TheNumeral)\r\n\u00a0\u00a0\u00a0\u00a0SELECT x.y.value('@x','int')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @AttributeBasedXMLList.nodes('root\/y') AS x(y)\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'finished',@Values\r\n\u00a0\u00a0--use the XML Shred trick\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'Attribute-based XML (@)',@Values\r\n\u00a0\u00a0INSERT INTO @ByteBucket5 (TheNumeral)\r\n\u00a0\u00a0\u00a0\u00a0SELECT x.y.value('.','int')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM @AttributeBasedXMLList.nodes('root\/y\/@x') AS x(y)\r\n\u00a0\u00a0INSERT INTO @log (TheEvent, [Values]) SELECT 'finished',@Values\r\n\u00a0\u00a0SELECT @Values=@Values+100\r\nEND\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\r\n--Yes, we need to check that they all agree!\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket\r\nUNION ALL\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket2\r\nUNION ALL\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket3\r\nUNION ALL\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket4\r\nUNION ALL\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket5\r\nUNION ALL\r\nSELECT COUNT(*), SUM(TheNumeral) FROM @ByteBucket6\r\n\r\nSELECT\r\n\u00a0\u00a0TheStart.[Values],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Using Split function'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAndTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Using Split function],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Element-based XML'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAnddTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Element-based XML],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Using Values script'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAndTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Using Values script],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Attribute-based XML'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAndTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [XML Attributes],*\/\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Element-based XML (text())'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAndTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Element-based XML (text())],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Attribute-based XML (@)'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN DATEDIFF( ms, TheStart.DateAndTime, Theend.DateAndTime )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Attribute-based XML (@)]\r\nFROM\u00a0\u00a0 @log TheStart\r\n\u00a0\u00a0INNER JOIN @log Theend\r\n\u00a0\u00a0\u00a0\u00a0ON Theend.Log_Id = TheStart.Log_Id + 1\r\nWHERE TheStart.TheEvent&lt;&gt;'finished'\r\nGROUP BY TheStart.[Values];\r\n\r\nSELECT\r\n\u00a0\u00a0TheStart.[Values],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Using Split function'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 END) AS [Using Split function],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Element-based XML'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Element-based XML],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Using Values script'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Using Values script],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Attribute-based XML'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [XML Attributes],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Element-based XML (text())'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Element-based XML (text())],\r\n\u00a0\u00a0MAX(CASE WHEN TheStart.TheeVent ='Attribute-based XML (@)'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 THEN theEnd.cpu-TheStart.cpu\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ELSE 0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END) AS [Attribute-based XML (@)]\r\nFROM\u00a0\u00a0 @log TheStart\r\n\u00a0\u00a0INNER JOIN @log Theend\r\n\u00a0\u00a0\u00a0\u00a0ON Theend.Log_Id = TheStart.Log_Id + 1\r\nWHERE TheStart.TheEvent&lt;&gt; 'finished'\r\nGROUP BY TheStart.[Values];\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>(Updated 14th Jan 2012, \u00a026th Jan 2012, and 3rd Aug 2017) Every so often, before SQL Server 2016, \u00a0the question came up on forums of how to pass a list as a parameter to a SQL procedure or function. Nowadays, of course, one uses the STRING_SPLIT() function \u00a0 &#8230;. DECLARE @tags VARCHAR(200) = &#8216;Yan,Tyan,Tethera,Methera,Pimp,Sethera,Lethera,Hovera,Dovera,Dik,Yanadik,Tyanadik,Tethera dik,Methera&#8230;&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"coauthors":[6813],"class_list":["post-3472","post","type-post","status-publish","format-standard","hentry","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3472","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=3472"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3472\/revisions"}],"predecessor-version":[{"id":71980,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3472\/revisions\/71980"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=3472"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=3472"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=3472"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=3472"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}