sql - 优化 SSIS 数据流任务中使用的 XQuery-heavy SQL 查询
问题描述
该查询用于每晚将大约 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 小时才能完成。有关如何优化此查询的任何建议?联接不能是内部联接。
解决方案
我只想提一下,这个建议没有考虑可以添加到数据库中 XML 的可能索引。根据您未显示的其他六个查询和许多其他因素,索引 XML 数据可能也是一件好事。我在这里给出的建议实际上只是一般的 X 查询建议,几乎适用于任何 X 查询表达式。
另外值得注意的是,在 SQL 数据库中存储和使用 XML 与关系数据一起进行过滤是一个坏主意,尤其是当您计划使用该数据进行大规模 ETL 解决方案时。正如您已经体验过的那样,这将是一件麻烦事。如果您仍处于可以更改的阶段,我强烈建议您这样做。
除此之外,这里有几个建议:
WHERE df2.XMLValue.value('(Item/*[local-name()="ExternalCode"])[1]', 'nvarchar(50)') IN('EXTERNAL_CODE1')
首先,应该重写过滤器表达式以使用exist
运算符(Microsoft doc here)。每个微软(这里):
出于性能原因,不要在谓词中使用 value() 方法与关系值进行比较,而是将 exists() 与 sql:column() 一起使用
其次,我会将新创建的exist
表达式移动到连接子句而不是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
在 final 的语句中使用该新节点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
路径表达式可能会有所不同,但使用您不希望像(Item/*[local-name()="CustomData"]/root/D/Number)
每个value
表达式那样遍历复杂路径的想法,最终肯定会有助于提高性能。
推荐阅读
- wget - wget:抓取网页时保存URL
- python-3.x - 如何删除除括号和连字符(-)之外的所有特殊字符
- python-3.x - 在屏幕上放置一个 tkinter 标签几秒钟然后销毁它
- kubernetes - k8s/istio - 在 Envoy 不支持的情况下,服务之间的 HTTP 缓存的替代方案是什么?
- java - 错误:jdk-13.0.2 中不存在包 com.sun.javadoc
- java - 我怎样才能让这个代码打印一个数字中有多少个质数?
- android - 在 Kotlin 中使用协程 IO 线程时使用的主线程
- unit-testing - 如何在 It 块内使用嘲笑模拟代码
- php - laravel 获取 5 小时后的数据
- node.js - 将 Node.js 应用(托管在 GAE)的 IP 列入白名单以访问 MongoDB Atlas(托管在 GCP)