【问题标题】:SQL Server: OPENXML vs SELECT..FROM when dealing with XML?SQL Server:处理 XML 时的 OPENXML 与 SELECT..FROM?
【发布时间】:2012-03-08 07:51:59
【问题描述】:

我有这个 xml:

DECLARE @x XML
SET @x = 
    '<data>
       <add>a</add>
       <add>b</add>
       <add>c</add>
     </data>';

任务:

我想列出a,b,c

方法一:

SELECT s.value('.', 'VARCHAR(8000)') AS [ADD]
FROM   @x.nodes('/data/add') AS t(s) 

方法 2:

DECLARE @idoc INT
EXEC sp_xml_preparedocument @idoc OUTPUT, @x

SELECT *
FROM   OPENXML(@idoc, '/data/add', 2)
       WITH ([add] NVARCHAR(MAX) '.')

他们都给我:

问题:

哪个是首选方式?

后者与前者相比(或反之亦然)有什么优势吗?

【问题讨论】:

  • 我个人更喜欢方法#1——它对我来说更直观,并且不需要sp_xml_preparedocument等额外的步骤——只是更容易使用
  • @marc_s 还有...意味着 #1 可以在视图等中使用。

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


【解决方案1】:

一个简单的测试表明,您的方法 1方法 2 花费的时间更少。我不会得出任何关于它总是如此的结论。这取决于您的 XML 的结构以及您需要如何查询 XML。

要测试的存储过程:

create procedure TestXML
  @X xml
as
set nocount on

select X.N.value('.', 'varchar(8000)')
from @X.nodes('/root/item') as X(N)

go

create procedure TestOpenXML
  @X xml
as
set nocount on

declare @idoc int
exec sp_xml_preparedocument @idoc out, @X

select value
from openxml(@idoc, '/root/item',1) 
  with (value  varchar(8000) '.')

exec sp_xml_removedocument @idoc

测试:

declare @X xml

set @X =
  (
    select number as '*'
    from master..spt_values
    for xml path('item'), root('root'), type
  )

set statistics time on
exec TestXML @X
exec TestOpenXML @X

结果方法一:

SQL Server Execution Times:
   CPU time = 63 ms,  elapsed time = 70 ms.

结果方法二:

SQL Server Execution Times:
   CPU time = 156 ms,  elapsed time = 159 ms.

(在 SQL Server 2005 上测试。)

【讨论】:

  • 如果@PraVn 反其道而行之,怎么能花更少的时间?
  • @RoyiNamir – 查询成本与执行时间不同,当涉及到 XML 查询时,查询成本是不可信的。
  • (只是为了一个想法)-您将如何解决这个问题? stackoverflow.com/questions/10818115/…(不打扰回答,用文字回答...)谢谢您的宝贵时间。
  • @RoyiNamir 做了一些测试,使用unpivot 看起来比其他答案更有效。我只看了执行计划。未经大量数据测试。
  • @RoyiNamir 谢谢。我很高兴能帮上忙。
【解决方案2】:

我更喜欢#2。尝试执行计划,发现第一个方法的成本为 97%,而第二个方法的成本仅为 3%

【讨论】:

  • 感谢您的努力:附言3%相对于批次。批次是什么?剩下的 97% 在哪里?
  • 在我看来,在使用 XML 数据类型时,查询成本估算是不可信的。
  • @RoyiNamir 我在一个批次中运行了两个查询。第一个为 97%,第二个为 3%
【解决方案3】:
SET NOCOUNT ON;
DECLARE @BankXml VARCHAR(MAX) = '<ROOT><ITEM BAF="HI" /></ROOT>'
DECLARE @ErrMsg VARCHAR(MAX) ='',@XmlId INT,@TranCount INT
CREATE TABLE #tmptbl(BAF VARCHAR(10))
IF (@BankXml IS NOT NULL)        
    BEGIN        
        EXEC SP_XML_PREPAREDOCUMENT @XmlId OUTPUT, @BankXml
        INSERT INTO #tmptbl(BAF)
        SELECT BAF
        FROM OPENXML(@XmlId, 'ROOT/ITEM', 1) WITH
        (
            BAF VARCHAR(10)
        )                           
    END 
BEGIN TRY
    IF @@TRANCOUNT = 0
        SET @TranCount = 1
    IF @TranCount=1 
        BEGIN TRAN
    IF 1=1
    BEGIN
        SELECT BAF FROM #tmptbl
    END     
    IF @TranCount = 1
        COMMIT TRAN
END TRY
BEGIN CATCH
    IF @@TRANCOUNT = 1 AND @TranCount = 1
    ROLLBACK TRAN
    SET @ErrMsg = 'Error : ' + @ErrMsg + ' : ' + ERROR_MESSAGE()
    RAISERROR(@ErrMsg,16,1)
END CATCH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    • 1970-01-01
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    相关资源
    最近更新 更多