There are some puzzles in the query plan that need to be sorted first. What does a computational scalar do and why does a stream aggregate exist.
The table table function returns a node table of shredded XML, one row for each shredded row. When you use typed XML, these columns are lvalue, lvaluebin, and tid. These columns are used in a computational scalar to calculate the actual value. The code there looks a little strange, and I canβt say that I understand why it is what it is, but the bottom line is that the xsd_cast_to_maybe_large function returns a value, and there is a code that handles the case when the value is equal and greater than 128 bytes.
CASE WHEN datalength( CONVERT_IMPLICIT(sql_variant, CONVERT_IMPLICIT(nvarchar(64), xsd_cast_to_maybe_large(XML Reader with XPath filter.[value], XML Reader with XPath filter.[lvalue], XML Reader with XPath filter.[lvaluebin], XML Reader with XPath filter.[tid],(15),(5),(0)),0),0))>=(128) THEN CONVERT_IMPLICIT(int,CASE WHEN datalength(xsd_cast_to_maybe_large(XML Reader with XPath filter.[value], XML Reader with XPath filter.[lvalue], XML Reader with XPath filter.[lvaluebin], XML Reader with XPath filter.[tid],(15),(5),(0)))<(128) THEN NULL ELSE xsd_cast_to_maybe_large(XML Reader with XPath filter.[value], XML Reader with XPath filter.[lvalue], XML Reader with XPath filter.[lvaluebin], XML Reader with XPath filter.[tid],(15),(5),(0)) END,0) ELSE CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(sql_variant, CONVERT_IMPLICIT(nvarchar(64), xsd_cast_to_maybe_large(XML Reader with XPath filter.[value], XML Reader with XPath filter.[lvalue], XML Reader with XPath filter.[lvaluebin], XML Reader with XPath filter.[tid],(15),(5),(0)),0),0),0) END
The same computational scalar for untyped XML is much simpler and more understandable.
CASE WHEN datalength(XML Reader with XPath filter.[value])>=(128) THEN CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0) ELSE CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0) END
If in value from more than value selection from lvalue else is extracted from value . In the case of untyped XML, the returned node table only displays the column identifiers, value, and lvalue.
When you use typed XML, the node's value store is optimized based on the data type specified in the schema. It looks like it can either appear in the value, lvalue or lvaluebin in the node table, depending on what type of value it and xsd_cast_to_maybe_large are to help figure it out.
The stream aggregate performs min () on the return values ββfrom the calculation scalar. We know, and SQL Server (at least sometimes) knows that someday only one row will be returned from the function associated with the table when you specify XPath in the value () function. The parser ensures that we build XPath correctly, but when the query optimizer looks at the evaluated lines, it will see 200 lines. The base grade for the table grade feature that parses XML is 10,000 rows, and then some adjustments are made using the XPath used. In this case, it ends in 200 rows, where there is only one. Pure speculation on my part is that the flow aggregate must take care of this mismatch. It will never aggregate anything, it sends only one row, which is returned, but it affects the power estimate for the whole branch and ensures that the optimizer uses 1 row as an estimate for this branch. This, of course, is really important when the optimizer chooses combining strategies, etc.
So what about 100 attributes? Yes, there will be 100 branches if you use the value function 100 times. But there are several optimizations here. I created a test setup to see which form and request form would be the fastest, using 100 attributes over 10 lines.
The winner was to use untyped XML and not use the nodes() function to clone r .
select X.value('(/r/@a1)[1]', 'int') as a1, X.value('(/r/@a2)[1]', 'int') as a2, X.value('(/r/@a3)[1]', 'int') as a3 from @T
There is also a way to avoid 100 branches with a hinge, but depending on what your actual request looks like, it might not be possible. The data type coming out of the turn must be the same. Of course, you can extract them as a string and convert them to the corresponding list in the column list. It also requires your table to have a primary / unique key.
select a1, a2, a3 from ( select T.ID,
Query schema for master query, 10 rows of 100 attributes:

Below are the results and the test setup I used. I tested 100 attributes and 10 rows and all int attributes.
Result:
Test Duration (ms) -------------------------------------------------- ------------- untyped XML value('/r[1]/@a') 195 untyped XML value('(/r/@a)[1]') 108 untyped XML value('@a') cross apply nodes('/r') 131 untyped XML value('@a') cross apply nodes('/r[1]') 127 typed XML value('/r/@a') 185 typed XML value('(/r/@a)[1]') 148 typed XML value('@a') cross apply nodes('/r') 176 untyped XML pivot 34 typed XML pivot 52
the code:
drop type dbo.TRABType drop type dbo.TType; drop xml schema collection dbo.RAB; go declare @NumAtt int = 100; declare @Attribs nvarchar(max); with xmlnamespaces('http://www.w3.org/2001/XMLSchema' as xsd) select @Attribs = ( select top(@NumAtt) 'a'+cast(row_number() over(order by 1/0) as varchar(11)) as '@name', 'sqltypes:int' as '@type', 'required' as '@use' from sys.columns for xml path('xsd:attribute') )