FOR XML
introducerades i SQL Server 2000.
SQL Server 2000 hade inte MAX
datatyper eller XML
data typ. Det var inte heller möjligt att använda FOR XML
i en underfråga.
Artikeln Vad returnerar serversidan FOR XML? förklarar
I SQL Server 2000 ...
FOR XML
... implementerades i kodlagret mellan frågeprocessorn och datatransportlagret ... frågeprocessorn producerar resultatet på samma sätt som utanFOR XML
och sedanFOR XML
kod formaterar raduppsättningen som XML. För maximal XML-publiceringsprestandaFOR XML
gör ångande XML-formatering av den resulterande raduppsättningen och skickar dess utdata direkt till serversidans TDS-kod i små bitar utan att buffra hela XML i serverutrymmet. Bitstorleken är 2033 UCS-2-tecken. Således skickas XML större än 2033UCS-2 tecken till klientsidan i flera rader som var och en innehåller en bit av XML. SQL Server använder ett fördefinierat kolumnnamn för denna raduppsättning med en kolumn av typenNTEXT
-“XML_F52E2B61-18A1-11d1-B105-00805F49916B
” – för att indikera chunked XMLrowset i UTF-16-kodning.
Så det verkar som att detta fortfarande är implementerat på samma sätt för toppnivå FOR XML
även i senare versioner.
SQL Server 2005 introducerade möjligheten att använda FOR XML
i underfrågor (vilket betyder att dessa nu måste hanteras av frågeprocessorn snarare än ett lager utanför den medan resultaten streamas till klienten)
Samma artikel förklarar att dessa kommer att skrivas som NVARCHAR(MAX)
eller XML
beroende på närvaron eller inte av en type
direktiv.
Förutom skillnaden i datatyp betyder detta den ytterligare SELECT
wrapper kan göra en drastisk skillnad i prestanda om #tab
är stor.
/*Can be streamed straight out to client without using server storage*/
SELECT col
FROM #tab
FOR XML AUTO
/*XML constructed in its entirety in tempdb first*/
SELECT(SELECT col
FROM #tab
FOR XML AUTO) AS wrapped_subquery
Det är möjligt att se de olika tillvägagångssätten i anropsstacken samt genomförandeplaner.
Strömma direkt
sqllang.dll!CXMLExecContext::AddTagAndAttributes() + 0x5a9 bytes
sqllang.dll!CXMLExecContext::AddXMLRow() + 0x2b7 bytes
sqltses.dll!CEsExec::FastMoveEval() + 0x9c bytes
sqllang.dll!CXStmtQuery::ErsqExecuteQuery() + 0x280 bytes
sqllang.dll!CXStmtXMLSelect::WrapExecute() + 0x2d7 bytes
sqllang.dll!CXStmtXMLSelect::XretDoExecute() + 0x355 bytes
sqllang.dll!CXStmtXMLSelect::XretExecute() + 0x46 bytes
sqllang.dll!CMsqlExecContext::ExecuteStmts<1,1>() + 0x368 bytes
sqllang.dll!CMsqlExecContext::FExecute() + 0x6cb bytes
sqllang.dll!CSQLSource::Execute() + 0x3ee bytes
sqllang.dll!process_request() + 0x757 bytes
Med underfråga
sqllang.dll!CXMLExecContext::AddTagAndAttributes() + 0x5a9 bytes
sqllang.dll!CXMLExecContext::AddXMLRow() + 0x2b7 bytes
sqllang.dll!CForXmlSerialize::ProcessRow() + 0x19 bytes
sqllang.dll!CUDXR_Base::PushRow() + 0x30 bytes
sqlmin.dll!CQScanUdx::Open() + 0xd5 bytes
sqlmin.dll!CQueryScan::StartupQuery() + 0x170 bytes
sqllang.dll!CXStmtQuery::SetupQueryScanAndExpression() + 0x391 bytes
sqllang.dll!CXStmtQuery::InitForExecute() + 0x34 bytes
sqllang.dll!CXStmtQuery::ErsqExecuteQuery() + 0x217 bytes
sqllang.dll!CXStmtSelect::XretExecute() + 0xed bytes
sqllang.dll!CMsqlExecContext::ExecuteStmts<1,1>() + 0x368 bytes
sqllang.dll!CMsqlExecContext::FExecute() + 0x6cb bytes
sqllang.dll!CSQLSource::Execute() + 0x3ee bytes
sqllang.dll!process_request() + 0x757 bytes
Båda anropar samma underliggande XML-kod men den "oupppackade" versionen har inga XML-iteratorer i själva planen, resultatet uppnås genom att ersätta metodanrop från CXStmtSelect
med CXStmtXMLSelect
istället (representerad i planen som en XML Select-rotnod snarare än en vanlig gammal Select).
På SQL Server 2016 CTP3 ser jag fortfarande ntext
för toppnivå FOR XML
. Men toppnivå FOR JSON
visas som nvarchar(max)
Åtminstone i CTP:n innehåller JSON-specialkolumnnamnet fortfarande GUID F52E2B61-18A1-11d1-B105-00805F49916B
trots att ursprunget till detta är IXMLDocument Interface.
Planerna ser ungefär likadana ut även om XML Select ersätts med en JSON Select
BTW:På build Microsoft SQL Server 2014 - 12.0.4213.0 (X64)
Jag ser ingen skillnad i beteende mellan temporära tabeller och permanenta tabeller. Detta beror förmodligen på den olika @@Version
mellan miljöerna använder din fråga http://sqlfiddle.com/ (12.0.2000.8) och https://data.stackexchange.com/ (12.0.4213.0).
Kanske har ett fel åtgärdats i sys.dm_exec_describe_first_result_set
mellan de två 2014-byggen.
2012 får jag samma resultat som Shnugo 11.0.5343.0 (med NULL
i de första tre raderna) men efter att ha installerat SP3 11.0.6020.0 får jag samma resultat som dina första resultat som visas i frågan.