3

Running SQL Server 2014. I have a stored procedure that returns a quite large XML. It goes something like this:

SELECT(
   ...
   FOR XML PATH (N''), ROOT, TYPE

Now, that query runs in 1 second. If I remove TYPE it runs in around half the time:

SELECT(
   ...
   FOR XML PATH (N''), ROOT

Obviously, the latter returns an nvarchar(max) instead of an xml. I want xml data, but if I ask for xml it gets slower! If I want to fetch xml data on the client, is it really necessary to convert it to xml using the TYPE directive above?

Q: Anyway, why is FOR XML ... TYPE significantly slower than FOR XML ...? Is there any way to improve the conversion?

4
  • 1
    First, 2 times difference isn't usually considered significant in the database world. The difference between PATH() and AUTO() modes, for example, is significant. Second, have you tried to compare them by running in the reverse order, and / or with cold data cache?
    – Roger Wolf
    Commented Jan 7, 2016 at 15:14
  • I dropped the stored procedure between the runs. Should reset any cache, no? So would AUTO be faster?
    – l33t
    Commented Jan 7, 2016 at 15:40
  • No, I mean using dbcc dropcleanbuffers after each execution to ensure that no data cached previously would skew the comparison.
    – Roger Wolf
    Commented Jan 8, 2016 at 2:44
  • And for xml auto() usually tends to be much slower than path(), not vice versa.
    – Roger Wolf
    Commented Jan 8, 2016 at 2:45

1 Answer 1

2

Did you try to set variables with the results as XML and as VARCHAR(MAX) without displaying them? Maybe the time difference you measure is bound to preparing the viewer? Pasting the first letters into a grid column is faster than creating a well formed, indented, displayable XML...

Sepcifying "TYPE" is not needed in most cases. You really need this with nested XML only. Just play around with aliases, PATH- and ROOT-literals and - of course - with or without TYPE:

And - very important! - try to call this with the surrounding SELECT and without:

SELECT 
(
    SELECT tbls.TABLE_NAME AS [@TableName]
          ,(
             SELECT COLUMN_NAME AS [@ColumName]
             FROM INFORMATION_SCHEMA.COLUMNS AS cols 
             WHERE cols.TABLE_NAME=Tbls.TABLE_NAME
             FOR XML PATH('COLUMN') /*,TYPE*/ 
           ) /*AS alias*/
    FROM INFORMATION_SCHEMA.TABLES AS Tbls
    FOR XML PATH('TABLE'),ROOT('ALL_TABLES') /*,TYPE*/ 
) /*AS alias*/

I don't know, how you continue with your generated XML. If you transfer it to your application it will be a plain string anyway.

Conclusio: Take the faster approach :-)

By the way...

I do not know your Stored Procedure and what else is done there besides the SELECT...

In most cases it is a bad habit to use SPs just to read data.

If your SP is not more than a wrapper around your SELECT you should think about a (single-statement!) table valued function to retrieve your data.

This function is easily queried and transformed to XML with

SELECT *
FROM dbo.MyFunction(/*Parameters*/)
FOR XML PATH('TheRowsName'),ROOT('TheRootName') [,TYPE]

Or - if you need this as XML everytime, you might define a scalar function delivering XML or VARCHAR(MAX). The re-usability of functions is way better than with SPs...

4
  • I concluded that it is the conversion to xml that takes time. I'm wondering if the conversion gives you anything of value. Xml syntax validation perhaps?
    – l33t
    Commented Jan 22, 2016 at 17:20
  • @l33t, yes XML conversions gives you validation. But you can rely on FOR XML PATH() creating valid XML in any case, so there's no extra value. Did you try the example of my answer? The biggest difference is the way a nested (sub)select is integrated in surrounding XML. Commented Jan 22, 2016 at 17:30
  • Will try it as soon as I can! Since the xml is already validated I would be surprised if the conversion is indeed slow. Maybe SSMS takes some time to render/initialize the cell with xml. But I don't see how that can affect STATISTICS TIME.
    – l33t
    Commented Jan 22, 2016 at 17:44
  • 1
    @l33t I was curious. I just tried four calls, all with a following dbcc dropcleanbuffers: Without TYPE, with TYPE, again with TYPE and again without TYPE. All four calls take 25% of the time and lead to identical Execution Plans. So - as expected - the difference comes from somewhere else... Commented Jan 22, 2016 at 22:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.