【发布时间】:2014-12-20 07:46:55
【问题描述】:
我在 SQL Server 2008 R2 中有一组 SQL 过程,最终生成一个大型 XML 文件,由 SSIS 收集并保存到客户的 ftp 文件夹中。对于相对较小的数据集,一切正常,但对于大数据集,性能非常糟糕;如果可以的话,我需要让事情变得更快。
这里是每次生成 xml 时所采取的当前步骤的摘要(所有步骤都由 SSIS 包调用,最终取回 xml 并保存它):
- 从 Live 数据库收集到我的数据库中的数据
- 数据转换为另一个表(称为 Proto_XML),因此它具有
FOR XML EXPLICIT工作所需的结构(列名称如[ParentName!2!TagName!ELEMENT]、大量重复数据、大量空值以及每个标签和父 ID行,但所有列上的索引稍后用于对结果进行排序)。 -
SELECT * FROM Proto_XML ORDER BY... FOR XML EXPLICIT对 Proto_XML 表执行查询以生成 xml 文件。 - 在 xml 上运行大约 12 个 xquery 行来清理它。它们大多看起来像这样的东西:
SET @XML.modify('delete //Data31/*//*[empty(.//text())]')。我希望我可以避免这一步,尤其是因为它涉及使用无类型的 XML,但至少在某些地方没有找到没有空元素的 XML 的创建方法。 - 尝试根据架构验证 xml;如果成功,则返回 xml。我已经避免将架构实际应用于 xml,因为这样做会将格式化的数字转换为它们的规范值。
我查看了很多关于如何加快最后三个步骤的信息,这些步骤都在同一个过程中(对于大文件,需要 6 个多小时!)。但是,在这种情况下,我无法看到如何应用这些建议;鉴于 XML 的大小和复杂性,我真的认为我无法像某些网站建议的那样使用 FOR XML AUTO 或 FOR 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