FOR XML
è stato introdotto in SQL Server 2000.
SQL Server 2000 non disponeva di MAX
tipi di dati o XML
tipo di dati. Né era possibile utilizzare FOR XML
in una sottoquery.
L'articolo Cosa restituisce il lato server FOR XML? spiega
In SQL Server 2000 ... FOR XML
... è stato implementato nel livello di codice tra il Query Processor e il livello di trasporto dei dati ... il Query Processor produce il risultato allo stesso modo di senzaFOR XML
e poi FOR XML
code formatta il set di righe come XML. Per le massime prestazioni di pubblicazione XML FOR XML
esegue la formattazione XML a vapore del set di righe risultante e invia direttamente il suo output al codice TDS lato server in piccoli blocchi senza memorizzare nel buffer l'intero XML nello spazio del server. La dimensione del blocco è di 2033 caratteri UCS-2. Pertanto, l'XML di dimensioni superiori a 2033 UCS-2 caratteri viene inviato al lato client in più righe, ciascuna contenente un blocco dell'XML. SQL Server utilizza un nome di colonna predefinito per questo set di righe con una colonna di tipo NTEXT
-“XML_F52E2B61-18A1-11d1-B105-00805F49916B
” – per indicare un set di righe XML in blocchi nella codifica UTF-16.
Quindi sembra che questo sia ancora implementato allo stesso modo per FOR XML
di livello superiore anche nelle versioni successive.
SQL Server 2005 ha introdotto la possibilità di utilizzare FOR XML
nelle sottoquery (il che significa che queste ora devono essere gestite dal Query Processor piuttosto che da un livello esterno durante lo streaming dei risultati al client)
Lo stesso articolo spiega che questi verranno digitati come NVARCHAR(MAX)
o XML
dipendente dalla presenza o meno di un type
direttiva.
Oltre alla differenza del tipo di dati, ciò significa l'aggiunta di SELECT
wrapper può fare una drastica differenza in termini di prestazioni se #tab
è grande.
/*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
È possibile vedere i diversi approcci negli stack di chiamate e nei piani di esecuzione.
Streaming diretto
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
Con sottointerrogazione
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
Entrambi finiscono per chiamare lo stesso codice XML sottostante ma la versione "non imballata" non ha iteratori XML nel piano stesso, il risultato si ottiene sostituendo le chiamate al metodo da CXStmtSelect
con CXStmtXMLSelect
invece (rappresentato nel piano come un nodo radice XML Select anziché un semplice vecchio Select).
Su SQL Server 2016 CTP3 vedo ancora ntext
per il livello superiore FOR XML
. Tuttavia FOR JSON
di livello superiore si presenta come nvarchar(max)
Almeno nel CTP il nome della colonna speciale JSON contiene ancora il GUID F52E2B61-18A1-11d1-B105-00805F49916B
nonostante il fatto che l'origine di questo sia l'interfaccia IXMLDocument.
I piani sembrano più o meno gli stessi anche se XML Select viene sostituito con un JSON Select
A proposito:su build Microsoft SQL Server 2014 - 12.0.4213.0 (X64)
Non vedo alcuna differenza nel comportamento tra tabelle temporanee e tabelle permanenti. Questo è probabilmente dovuto alla diversa @@Version
tra gli ambienti la tua domanda utilizza http://sqlfiddle.com/ (12.0.2000.8) e https://data.stackexchange.com/ (12.0.4213.0).
Forse è stato corretto un bug in sys.dm_exec_describe_first_result_set
tra le due build del 2014.
Nel 2012 ottengo gli stessi risultati di Shnugo su 11.0.5343.0 (con NULL
nelle prime tre righe) ma dopo aver installato SP3 11.0.6020.0 ottengo gli stessi risultati iniziali mostrati nella domanda.