【问题标题】:Optimizing a XQuery-heavy SQL query used in SSIS data flow task优化 SSIS 数据流任务中使用的 XQuery-heavy SQL 查询
【发布时间】:2021-11-20 19:06:55
【问题描述】:

此查询用于每晚将大约 600,000 行数据导入数据仓库中的目标表。每次导入前都会截断目标表。

SELECT -- Around 70 fields from MainTable which contains around 600,000 rows
-- Around 150 fields from around 50 various tables, some quite big
-- Around 35 fields from XQuery derived table queries such as dt_EXTERNAL_CODE1
FROM MainTable
LEFT JOIN -- Around 50 tables
LEFT JOIN
(
SELECT df.ParentID,
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/A/Number)[1]', 'float'),0) AS a, 
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/B/Number)[1]', 'float'),0) AS b,
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/C/Number)[1]', 'float'),0) AS c,
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/D/Number)[1]', 'float'),0) AS d,
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/E/Number)[1]', 'float'),0) AS e,
ISNULL(df2.XMLValue.value('(Item/*[local-name()="CustomData"]/root/F/Number)[1]', 'float'),0) AS f
FROM DynamicField df
INNER JOIN DynamicField df1 ON df.DynamicFieldID = df1.DynamicFieldID
INNER JOIN DynamicField df2 ON df1.DynamicFieldID = df2.ParentID
WHERE df2.XMLValue.value('(Item/*[local-name()="ExternalCode"])[1]', 'nvarchar(50)') IN('EXTERNAL_CODE1')
) dt_EXTERNAL_CODE1 ON MainTable.DynamicFieldID = dt_EXTERNAL_CODE1.ParentID
LEFT JOIN -- 6 more like the derived table query above, but with some other external code

SSIS 导入作业大约需要 10 小时才能完成。有关如何优化此查询的任何建议?联接不能是内部联接。

【问题讨论】:

  • trenton 提供了一个深思熟虑的答案,但是,您为什么不先将 XML 暂存到关系表中呢?那必须更快。
  • 检查瓶颈在哪里 - 如果在 SSMS 中运行查询,需要 10 个小时吗?接下来,运行包但不要在任何地方插入数据,用派生列或不做任何事情的东西替换目标。让我们知道您发现了什么,瓶颈可能是源查询、打包或插入数据。

标签: sql sql-server ssis query-optimization xquery


【解决方案1】:

我只想提一下,这个建议没有考虑可能添加到数据库中 XML 的索引。根据您未显示的其他六个查询和许多其他因素,索引 XML 数据可能也是一件好事。我在这里给出的建议实际上只是一般的 X-query 建议,几乎适用于任何 X-query 表达式。

另外值得注意的是,在 SQL 数据库中存储和使用 XML 进行过滤以及关系数据是一个坏主意,尤其是当您计划使用该数据进行大规模 ETL 解决方案时。正如您已经体验过的那样,这将是一件麻烦事。如果您仍处于可以更改的阶段,我强烈建议您这样做。

除此之外,这里有几个建议:

首先,过滤器表达式WHERE df2.XMLValue.value('(Item/*[local-name()="ExternalCode"])[1]', 'nvarchar(50)') IN('EXTERNAL_CODE1') 应重写为使用exist 运算符(Microsoft 文档here)。每个微软 (here):

出于性能原因,不要在谓词中使用 value() 方法与关系值进行比较,而是使用 exists() 和 sql:column()

其次,我会将新创建的exist 表达式移动到join 子句而不是WHERE 子句。当我查看此查询时,优化器可能会在实际执行联接之前将您的过滤器应用于整个 DynamicField df2 表。根据这些连接的基数,这可能对性能非常不利。我想您希望仅对从表达式FROM DynamicField df INNER JOIN DynamicField df1 ON df.DynamicFieldID = df1.DynamicFieldID 返回的行执行此过滤器。这里的要点是减少将使用任何 XML 过滤的记录数量将大大提高性能

第三,对value() 的每次调用都将实例化一个新的XML 阅读器,该阅读器需要遍历(Item/*[local-name()="CustomData"]/root/D/Number) 路径。减少 XML 读取器的每个实例为检索 SELECT 所需的值而必须执行的工作量将大大提高性能。如果您要遍历的重复路径(例如您的示例),则最好使用附加的 OUTER APPLY 运算符来调用 query 来检索 XML 元素 root 作为单独的节点,然后使用该新的在您的 value 语句中的最后一个 SELECT 中的节点。像这样的:

SELECT 
   df.ParentID
    ,ISNULL(root.RootXmlFrag.value('(root/A/Number)[1]', 'float'),0) AS a
    ...... 
FROM 
    DynamicField df
    INNER JOIN DynamicField df1 ON df.DynamicFieldID = df1.DynamicFieldID
    INNER JOIN DynamicField df2 ON df1.DynamicFieldID = df2.ParentID
    OUTER APPLY df2.XMLValue.query('(Item/*[local-name()="CustomData"]/root)[1]') AS root(RootXmlFrag)

实际的最终query 路径表达式可能会有所不同,但如果您不希望为每个value 表达式遍历像(Item/*[local-name()="CustomData"]/root/D/Number) 这样的复杂路径,那么最终肯定会提高性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 2020-05-05
    相关资源
    最近更新 更多