SELECT FOR XML AUTO并返回数据类型

Luk*_*zda 11 xml sql t-sql sql-server sql-server-2012

在玩游戏期间,sys.dm_exec_describe_first_result_set我到达这一点:

CREATE TABLE #tab(col INT, x XML );
INSERT INTO #tab(col,x) VALUES (1,NULL), (2,NULL), (3,'<a>x</a>');

SELECT 'Simple XML' AS description, name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT col
     FROM #tab
     FOR XML AUTO', NULL, 0)  
UNION ALL
SELECT 'Wrapped with subquery', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT(SELECT col
            FROM #tab
            FOR XML AUTO) AS wrapped_subquery', NULL, 0)
UNION ALL 
SELECT 'XML column', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT x FROM #tab ', NULL, 0)
UNION ALL
SELECT 'Casted XML', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT CAST(''<o>O</o>'' AS XML) AS x', NULL, 0)
UNION ALL
SELECT 'Wrapped Casted XML', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT (SELECT CAST(''<o>O</o>'' AS XML) AS x) AS wrapped', NULL, 0)
UNION ALL
SELECT 'Text value', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
N'SELECT CAST(''aaa'' AS NTEXT) AS text_string', NULL, 0)
UNION ALL
SELECT 'Wrapped Text Value', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
  N'SELECT (SELECT CAST(''aaa'' AS NTEXT)) AS text_string_wrapped', NULL, 0)
Run Code Online (Sandbox Code Playgroud)

LiveDemo

输出:

??????????????????????????????????????????????????????????????????????????????????????
?      Description      ?                   name                  ? system_type_name ?
??????????????????????????????????????????????????????????????????????????????????????
? Simple XML            ? XML_F52E2B61-18A1-11d1-B105-00805F49916 ? ntext            ?
? Wrapped with subquery ? wrapped_subquery                        ? nvarchar(max)    ?
? XML column            ? x                                       ? xml              ?
? Casted XML            ? x                                       ? xml              ?
? Wrapped Casted XML    ? wrapped                                 ? xml              ?
? Text value            ? text_string                             ? ntext            ?
? Wrapped Text Value    ? text_string_wrapped                     ? ntext            ?
??????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

和:

SELECT col        -- SSMS result grid - XML column
FROM #tab
FOR XML AUTO

SELECT(SELECT col  -- text column
       FROM #tab
       FOR XML AUTO) AS wrapped_subquery
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 为什么FOR XML AUTO不返回XML/NVARCHAR(MAX)数据类型但是ntext(不推荐使用数据类型!)?
  2. 如何使用子查询包装将数据类型更改ntextnvarchar(max)
  3. 为什么相同的规则不适用于XML/NTEXT列?

我知道我的问题可能是技术和内部操作,但我会很感激MSDN/Connect中的任何见解或文档?

编辑:

有趣的是,当我使用普通表(不是临时表)时,它返回所有ntext:

?????????????????????????????????????????????????????????????????????????????????????
?      description       ?                   name                ? system_type_name ?
?????????????????????????????????????????????????????????????????????????????????????
? Simple XML             ? XML_F52E2B61-18A1-11d1-B105-00805F499 ? ntext            ?
? Wrapped with subquery  ? wrapped_subquery                      ? ntext            ?
? XML column             ? x                                     ? ntext            ?
? Casted XML             ? x                                     ? ntext            ?
? Wrapped Casted XML     ? wrapped                               ? ntext            ?
? Text value             ? text_string                           ? ntext            ?
? Wrapped Text Value     ? text_string_wrapped                   ? ntext            ?
?????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

SqlFiddleDemo

根据TYPE directive:

SQL Server对xml(Transact-SQL)的支持使您可以通过指定TYPE指令,有选择地请求将FOR XML查询的结果作为xml数据类型返回.

SQL Server将XML数据类型实例数据作为不同服务器构造的结果返回给客户端,例如使用TYPE指令的FOR XML查询,或者xml数据类型用于从SQL表列返回XML实例数据值和输出参数.在客户端应用程序代码中,ADO.NET提供程序请求从服务器以二进制编码发送此XML数据类型信息.但是,如果使用不带TYPE指令的FOR XML,则XML数据将以字符串类型的形式返回.

和:

SELECT 'Simple XML' AS description, name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT col AS col
     FROM #tab
     FOR XML AUTO, TYPE', NULL, 0)  
UNION ALL
SELECT 'Wrapped with subquery', name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
   N'SELECT(SELECT col
            FROM #tab
            FOR XML AUTO,TYPE) AS wrapped_subquery', NULL, 0);
Run Code Online (Sandbox Code Playgroud)

LiveDemo

  1. 为什么ntext不在nvarchar(max)引用中the XML data comes back as a string type,普通/临时表的区别在哪里?

Mar*_*ith 6

FOR XML 是在SQL Server 2000中引入的.

SQL Server 2000没有MAX数据类型或XML数据类型.也不可能FOR XML在子查询中使用.

文章服务器端FOR XML返回什么?说明

在SQL Server 2000中...... FOR XML在查询处理器和数据传输层之间的代码层中实现...查询处理器以与没有相同的方式生成结果 FOR XML,然后FOR XML将行集代码格式化为XML.为了获得最大的XML发布性能FOR XML,可以对生成的行集进行XML格式化,并将其输出直接以小块的形式发送到服务器端TDS代码,而无需在服务器空间中缓冲整个XML.块大小为2033 UCS-2字符.因此,大于2033个UCS-2字符的XML以多行发送到客户端,每行包含一大块XML.SQL Server使用预定义的列名此行集与类型的一列NTEXT - XML_F52E2B61-18A1-11d1-B105-00805F49916B" " -以表示UTF-16编码XML分块的行集.

因此,对于FOR XML 更高版本的顶级版本,它仍然以相同的方式实现.

SQL Server 2005引入了FOR XML在子查询中使用的能力(这意味着这些现在需要由查询处理器处理而不是在其外部的层,同时将结果流式传输到客户端)

同一篇文章解释说,这些将被输入NVARCHAR(MAX)XML取决于type指令的存在与否.

除了数据类型的差异,这意味着额外的SELECT包装器可以在性能上产生巨大差异#tab.

/*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
Run Code Online (Sandbox Code Playgroud)

可以看到调用堆栈中的不同方法以及执行计划.

直接流式传输

在此输入图像描述

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    
Run Code Online (Sandbox Code Playgroud)

带子查询

在此输入图像描述

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
Run Code Online (Sandbox Code Playgroud)

两个最终调用相同的底层XML代码,但"展开"的版本没有在计划本身的任何XML迭代器,其结果是通过替换方法来实现从调用CXStmtSelectCXStmtXMLSelect代替(在俯视为XML选择根节点,而表示而不是一个普通的旧选择).


在SQL Server 2016 CTP3上,我仍然看到ntext顶级FOR XML.但顶级FOR JSON显示为nvarchar(max)

在此输入图像描述

至少在CTP中,JSON特殊列名仍然包含GUID,F52E2B61-18A1-11d1-B105-00805F49916B尽管它的起源是IXMLDocument接口.

虽然XML Select被JSON Select替换,但计划看起来大致相同

在此输入图像描述


顺便说一句:在构建时,Microsoft SQL Server 2014 - 12.0.4213.0 (X64)我没有看到临时表和永久表之间的行为有任何区别.这可能取决于@@Version您的问题使用http://sqlfiddle.com/(12.0.2000.8)和https://data.stackexchange.com/(12.0.4213.0)的环境之间的差异.

也许在sys.dm_exec_describe_first_result_set2014年的两个版本之间修复了一个错误.

在2012年,我得到了与Shnugo在11.0.5343.0上相同的结果(NULL在前三行中)但是在安装SP3 11.0.6020.0后,我得到了与问题中显示的初始结果相同的结果.

  • 真的很棒的文章 (2认同)