【问题标题】:SELECT FOR XML AUTO and return datatypesSELECT FOR XML AUTO 并返回数据类型
【发布时间】:2016-02-27 06:39:58
【问题描述】:

在玩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 列?

我知道我的问题可能与技术和内部运营有关,但我将不胜感激 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) 的支持使您可以选择 请求将 FOR XML 查询的结果通过指定 TYPE 指令作为 xml 数据类型返回。

SQL Server 将 XML 数据类型实例数据作为 不同服务器结构的结果,例如使用 FOR XML 查询 TYPE 指令,或者 xml 数据类型用于返回 XML 来自 SQL 表列和输出参数的实例数据值。在 客户端应用程序代码,ADO.NET 提供程序请求此 XML 数据 要从服务器以二进制编码发送的类型信息。 但是,如果您使用的 FOR XML 没有 TYPE 指令,则 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 以及正常/临时表的区别在哪里?

【问题讨论】:

标签: sql sql-server xml tsql sql-server-2012


【解决方案1】:

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 对 生成的行集并直接将其输出发送到服务器端 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) 或XML,具体取决于type 指令的存在与否。

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

/*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

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

直接流式传输

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    

带子查询

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 代码,但“未包装”版本在计划本身中没有任何 XML 迭代器,结果是通过将来自 CXStmtSelect 的方法调用替换为 CXStmtXMLSelect 来实现的(表示为计划作为 XML Select 根节点而不是普通的旧 Select)。


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

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

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


顺便说一句:在构建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 后,我得到的结果与问题中显示的初始结果相同。

【讨论】:

    【解决方案2】:

    有趣的问题! NTEXT 真奇怪!

    我对子查询有一个想法:当您返回一个 XML 时,它会作为字符串传输,除非您指定 TYPE(您肯定从带有 CROSS APPLY 的嵌套 XML 或从带有 STUFF 的字符串连接中知道这一点,其中你有时会看到 TYPE 和下面的 .value() - 有时是“裸体”。

    我无法真正重现您的结果(SQL Server 2012 也是如此)。简单的 copy'n'paste 回来了(我想用声明的表变量和函数的返回值进行测试):

    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
    

    编辑:有一个我认为不清楚的新观察结果,但这是我的错误......把它拿走了......

    【讨论】:

    • 尝试使用普通/临时表。 FOR XML 的结果以字符串形式返回。问题是为什么NTEXT 以及子查询如何神奇地改变类型NTEXT -&gt; NVARCHAR(MAX)
    • 你的@@VERSION 是什么?在构建 12.0.4213.0 时,我得到与问题中所述相同的结果。可能在某个时候在sys.dm_exec_describe_first_result_set 中修复了一个错误。
    • 您好!首先要说:不加评论的投票是 - 嗯 - 有点弱?其次要说:我的评论是在之前赏金开始的。 OP 添加了关于TYPE 关键字在我的贡献之后的编辑部分。我不确定,但我认为现有的支持来自 OP 的提示。无论如何,我不是为了赏金而钓鱼。这超出了我的认知。我只是非常感兴趣,并将关注所有答案以了解更多信息。
    • @MartinSmith, @@VERSION 带来(缩短):SS2012 - 11.0.5343.0 (X64) 4 2015 (64-bit) o​​n Windows NT 6.3 (Build 10240)
    • 是的,我也一样。我还没有安装 SP3,所以不确定 2012 年是否有修复。
    猜你喜欢
    • 1970-01-01
    • 2020-04-22
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多