【问题标题】:More efficient creation of XML file than SQL Server FOR EXPLICIT比 SQL Server FOR EXPLICIT 更高效地创建 XML 文件
【发布时间】:2014-12-20 07:46:55
【问题描述】:

我在 SQL Server 2008 R2 中有一组 SQL 过程,最终生成一个大型 XML 文件,由 SSIS 收集并保存到客户的 ftp 文件夹中。对于相对较小的数据集,一切正常,但对于大数据集,性能非常糟糕;如果可以的话,我需要让事情变得更快。

这里是每次生成 xml 时所采取的当前步骤的摘要(所有步骤都由 SSIS 包调用,最终取回 xml 并保存它):

  1. 从 Live 数据库收集到我的数据库中的数据
  2. 数据转换为另一个表(称为 Proto_XML),因此它具有 FOR XML EXPLICIT 工作所需的结构(列名称如 [ParentName!2!TagName!ELEMENT]、大量重复数据、大量空值以及每个标签和父 ID行,但所有列上的索引稍后用于对结果进行排序)。
  3. SELECT * FROM Proto_XML ORDER BY... FOR XML EXPLICIT 对 Proto_XML 表执行查询以生成 xml 文件。
  4. 在 xml 上运行大约 12 个 xquery 行来清理它。它们大多看起来像这样的东西:SET @XML.modify('delete //Data31/*//*[empty(.//text())]')。我希望我可以避免这一步,尤其是因为它涉及使用无类型的 XML,但至少在某些地方没有找到没有空元素的 XML 的创建方法。
  5. 尝试根据架构验证 xml;如果成功,则返回 xml。我已经避免将架构实际应用于 xml,因为这样做会将格式化的数字转换为它们的规范值。

我查看了很多关于如何加快最后三个步骤的信息,这些步骤都在同一个过程中(对于大文件,需要 6 个多小时!)。但是,在这种情况下,我无法看到如何应用这些建议;鉴于 XML 的大小和复杂性,我真的认为我无法像某些网站建议的那样使用 FOR XML AUTOFOR XML RAW。还有各种建议可以提高FOR XML EXPLICIT 转换的速度,但我不知道如何让FOR XML EXPLICIT 的事情变得更容易,而不是像我目前所做的那样直接引用格式正确的索引表。

this question 中提出了一个可能的解决方案,但我没有找到任何关于如何在 SSIS 中将完整结果集转换为 xml 文件的建议(可能使用脚本任务),如果答案是这样的话。项目早期还决定避免使用脚本任务来完成作业,因为(不正确?)认为这会比 SQL 还要慢。

那么,我的问题是——你会建议尝试什么以了解加快 xml 文件的创建速度——我是否应该在此步骤中使用 SQL Server 以外的其他东西?如果是,你有什么想法吗?关于我应该使用什么/我可以用来找出如何实现 xml 创建的任何关键字或链接?

这就是需要很长时间的FOR XML EXPLICIT 过程的本质(所有名称都已删除):

IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results

DECLARE @SchemaError NVARCHAR(1000)

CREATE TABLE #Results
( Value XML NOT NULL)

DECLARE
     @AvailableFields   VARCHAR(MAX) = ''
    ,@OrderBy           VARCHAR(MAX)
    ,@SQL               VARCHAR(MAX) = ''
    ,@Untyped_XML       XML
    ,@XML               XML(DataFeed_Schema)

---------------------------------------------------------------------------------------------------------------
-- We should attempt to select all fields from the available data table, unless restricted in the formatting of
-- the datafeed itself.
SELECT
    @AvailableFields = @AvailableFields + QUOTENAME(Col.name) + ','
FROM
    MyDataBase.sys.columns  AS Col
WHERE
    Col.object_id = OBJECT_ID('MyDataBase.dbo.Proto_XML')
-- ignore the identity column; we only want the ones that actually go in the XML, starting with "Tag"
AND Col.name <> 'Id'
ORDER BY
    Col.column_id

SELECT @AvailableFields = LEFT(@AvailableFields,LEN(@AvailableFields)-1)

SELECT @OrderBy = '
         [Level4!4!Name!ELEMENT]
        ,[Data6!6!Label!ELEMENT]
        ,[Data10!10!Name!ELEMENT]
        ,[Data12!12!Label!ELEMENT]
        ,[Data16!16!Name!ELEMENT]
        ,[Data18!18!Label!ELEMENT]
        ,[Data22!22!Number!ELEMENT]
        ,[Data23!23!Name!ELEMENT]
        ,[Data25!25!Name!ELEMENT]
        ,[Data29!29!Name!ELEMENT]
        ,[Data31!31!CreateDate!ELEMENT]
        ,[Data32!32!LastName!ELEMENT]
        ,[Data34!34!Label!ELEMENT]
        ,[Data37!37!Label!ELEMENT]
        ,[Data39!39]
        ,[Data40!40!Name!ELEMENT]
        ,[Data42!42!CreateDate!ELEMENT]
        ,[Data46!46!Id!ELEMENT]
        ,[Data48!48!Id!ELEMENT]
        ,[Data50!50!Id!ELEMENT]
        ,[Data53!53!Id!ELEMENT]
        ,[Data54!54]
        ,[Data55!55!Type!ELEMENT]
        ,Tag
        ,Parent'

---------------------------------------------------------------------------------------------------------------
-- Use our list of columns to select what we want into our temporary table (the table is just used so that a
-- variable can be referenced in the dynamic sql and in the main query).
SELECT @SQL =
'INSERT #Results(Value)
SELECT
    (SELECT ' + @AvailableFields + '
     FROM Proto_XML
     ORDER BY' + @OrderBy + '
     FOR XML EXPLICIT
    )'

PRINT (@SQL)
EXEC (@SQL)

-- Pop our newly created XML into a variable ready for its spring clean.
SELECT @Untyped_XML = Value FROM #Results

---------------------------------------------------------------------------------------------------------------
-- Delete all empty nodes below the __ Level
SET @Untyped_XML.modify('delete //Data31/*//*[empty(.//text())]')
-- Delete all empty nodes exactly one level below __ (This will leave the parent, but will not affect those
-- more levels below (e.g will not delete the empty parents left below __)
SET @Untyped_XML.modify('delete //Data22/*/*[empty(.//text())]')
-- do the same below __, __ and __
SET @Untyped_XML.modify('delete //Data54/*[empty(.//text())]')
SET @Untyped_XML.modify('delete //Data48/*[empty(.//text())]')
SET @Untyped_XML.modify('delete //Data46/*[empty(.//text())]')
-- We use "_" to indicate an empty label, so that the line above does not delete the parent __ level
-- from __. Now that we have done the step above, we can delete the __ that are so marked.
SET @Untyped_XML.modify('delete //Data29/Data33/Data34[. = "_"]')
SET @Untyped_XML.modify('delete //ClaimantInfo/CustomFields/CustomField[. = "_"]')
SET @Untyped_XML.modify('delete //Claim/CoverStatus/PurposeOfTrip[. = "_"]')
-- Delete all empty __, __ and __ empty custom fields.
SET @Untyped_XML.modify('delete //Data12/CustomFields/*[empty(.//text())]')
SET @Untyped_XML.modify('delete //Data10/CustomFields/*[empty(.//text())]')
SET @Untyped_XML.modify('delete //Data4/CustomFields/*[empty(.//text())]')
-- There are some mandatory fields - if these have empty indicators, they need to be emptied.
SET @Untyped_XML.modify('replace value of (//Data31/Data32[text() = "_"]/text())[1] with ""')
-- Add version and NameSpace information
SELECT @Untyped_XML = REPLACE(CONVERT(VARCHAR(MAX),@Untyped_XML),'<DataFeed>','<?xml version ="1.0"?>
<DataFeed xmlns ="http://tempuri.org/MyData.xsd">')
-- See if we can get the XML into the schema...
BEGIN TRY
    SELECT @XML = @UNTYPED_XML
END TRY
BEGIN CATCH
    SELECT @SchemaError =  ERROR_MESSAGE()
END CATCH

IF @SchemaError IS NULL
    SELECT @Untyped_XML AS DataFeedContent
ELSE
    SELECT @SchemaError AS 'ErrorEncountered', @Untyped_XML AS FailedXML

---------------------------------------------------------------------------------------------------------------
DROP TABLE #Results

【问题讨论】:

  • 看看FOR XML PATH - 比FOR XML EXPLICIT 的东西更简单但更强大..
  • 我没有设法用FOR XML PATH 重新创建我的 XML 的复杂性。这有两个可能的原因 - 'FOR XML PATH' 是有限的,或者我需要做更多的学习。我很乐意尝试了解更多信息,但它绝对能够生成复杂和定制的 XML 位吗?我只是质疑,因为我尝试过但失败了......

标签: sql xml sql-server-2008 optimization ssis


【解决方案1】:

您可以考虑在 SSIS 中使用 XML 任务并使用 XSLT 转换来转换 XML。过去,当我想引入 CDATA 部分时(由于客户要求),我已经这样做了,因为 FOR XML EXPLICIT 看起来工作量很大。

我很好奇最后三个步骤中哪一部分花费的时间最长。您能否分析您的存储过程并查看您的过程中哪些行占用了最高百分比的时间?

【讨论】:

  • 嗨@Ann,我会检查过程并在此处发布结果。参考您的答案,您的意思是我应该使用“FOR XML PATH”或 SQL Server 中的其他东西来制作一些快速、讨厌的 XML,然后将其提供给包并使用样式表将其转换为漂亮的 XML 吗?或者有没有办法在 SSIS 中将完整的结果集转换为 XML?
  • 好的,所以查看过程的实际执行计划,xquery modify 命令都占用了每个批处理成本的 12%。这加起来基本上是批处理成本的 100%……不幸的是,需要这些修改命令才能将 xml 放入架构中,因此我无法在命令运行之前将文件类型化为 XML。
  • @HighPlainsGrifter 我的想法是通过使用样式表实现与modify 命令相同的效果。但是您可以更进一步,生成一些更简单的 XML,然后使用样式表将其转换为您需要的内容。 (您甚至可以链接 XML 任务并连续应用多个样式表。)
  • @HighPlainsGrifter 顺便说一句,如果您可以访问 Profiler,我会运行它(过滤方式向下,因此您只捕获这个 proc)并查看它说的哪个命令占用的时间最多。执行计划可能非常有用,但它并不总是反映实际的运行时间。
  • @HighPlainsGrifter ...实际上,我注意到您有 12 个 modify 语句。 12 x 12% 是 144% ... 看起来有问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
  • 2010-11-11
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 2018-08-13
相关资源
最近更新 更多