SELECT FOR XML AUTO并返回数据类型

时间:2015-11-24 17:31:13

标签: sql sql-server xml tsql 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)

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            ║
╚═══════════════════════╩═════════════════════════════════════════╩══════════════════╝

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

问题:

  1. 为什么FOR XML AUTO不会返回XML/NVARCHAR(MAX)数据类型,而是ntext(不推荐使用数据类型!)?
  2. 子查询包装如何将数据类型从ntext更改为nvarchar(max)
  3. 为什么相同的规则不适用于XML/NTEXT列?
  4. 我知道我的问题可能是技术和内部操作,但我将不胜感激 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            ║
    ╚════════════════════════╩═══════════════════════════════════════╩══════════════════╝
    

    SqlFiddleDemo

    根据TYPE directive

      

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

         

    SQL Server将XML数据类型实例数据作为a返回给客户端   不同服务器结构的结果,例如使用的FOR XML查询   TYPE指令,或使用xml数据类型返回XML的位置   SQL表列和输出参数中的实例数据值。在   客户端应用程序代码,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);
    

    LiveDemo

    1. 为什么ntext不是nvarchar(max),而是引用the XML data comes back as a string type以及差异正常/临时表在哪里?

2 个答案:

答案 0 :(得分:6)

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

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

文章What does server side FOR XML return?解释

  

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

因此,对于以后版本中的顶级FOR XML,它仍然以相同的方式实现。

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

同一篇文章解释说,根据NVARCHAR(MAX)指令的存在与否,这些内容将被输入为XMLtype

除了数据类型的差异,这意味着如果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

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

直接投放

enter image description here

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    

使用子查询

enter image description here

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

两者最终都调用相同的底层XML代码,但“unwrapped”版本在计划本身中没有任何XML迭代器,结果是通过将CXStmtSelect的方法调用替换为CXStmtXMLSelect来实现的。相反(在计划中表示为XML Select根节点而不是普通的旧选择)。

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

enter image description here

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

虽然XML Select被替换为JSON Select

,但计划看起来大致相同

enter image description here

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

在2014年的两个版本中,sys.dm_exec_describe_first_result_set可能修复了一个错误。

2012年,我在11.0.5343.0获得了与Shnugo相同的结果(前三行中有NULL),但在安装SP3 11.0.6020.0后,我得到的结果与问题中显示的初始结果相同。 / p>

答案 1 :(得分:0)

有趣的问题! NTEXT真的很奇怪!

我对子查询有所了解:当你返回一个XML时,它会被转换为字符串,除非你指定TYPE(你肯定从嵌套的XML中用CROSS APPLY或者用STUFF的字符串连接知道这一点,你在某些地方看到这个TYPE.value() - 有时&#34;裸体&#34;。

我无法真正重现您的结果(SQL Server 2012)。简单的复制&#39; n&#39;粘贴回来(我想用声明的表变量测试并返回函数的值):

Simple XML              NULL                  NULL
Wrapped with subquery   NULL                  NULL
XML column              NULL                  NULL
Casted XML              x                     xml
Wrapped Casted XML      wrapped               xml
Text value              text_string           ntext
Wrapped Text Value      text_string_wrapped   ntext
编辑:有一个我认为不清楚的新观察,但这是我的错误......把它拿走了......