【问题标题】:Slow performance when doing SQL join on lots of split results对大量拆分结果执行 SQL 联接时性能下降
【发布时间】:2011-11-08 11:02:04
【问题描述】:

我有大约 30 个字符串传递给我的存储过程,它们充当我需要操作的表的列。每个字符串都是分隔的,我使用 split 函数将这些字符串中的每一个拆分成适当的行,这些行通常包含 20 行或更少的行。 split 函数非常快,并返回一个表结果,其中一列是主键 ID,另一列是拆分值,从检查执行计划来看,三十个拆分只占用了我一小部分时间。似乎影响我表现的部分是这 30 个拆分结果集的实际连接。

DECLARE @WorkingTab TABLE ([ID] INT PRIMARY KEY, [Col1] VARCHAR(255), [Col2] VARCHAR(255), ...)

INSERT INTO @WorkingTab ([ID], [Col1], [Col2], ...)
SELECT
    splitStr1.ID,
    splitStr1.VALUE,
    splitStr2.VALUE,
    .
    .
    .
FROM
    dbo.Split(@Str1, '~') splitStr1
    LEFT JOIN
        dbo.Split(@Str2, '~') splitStr2
    ON splitStr1.ID = splitStr2.ID
    LEFT JOIN
        dbo.Split(@Str3, '~') splitStr3
    .
    .
    .

我尝试使用内连接而不是左连接,这会稍微降低性能。我尝试仅将第一个拆分结果插入索引临时表,然后将其余列值更新到临时表中的适当列中,这再次导致性能略有下降。我尝试将所有结果插入到一个持久表中,但这根本没有提高性能。如果有人对解决此问题的其他方法或只是一般性能提示有任何建议,我会全神贯注。提前致谢。

【问题讨论】:

  • 这看起来是一种可怕的方式来完成你想要的!您能退后一步,告诉我们您要解决的实际问题吗...
  • 其中一个许多问题是不能使用索引。这是一个非常糟糕的方法,应该重新设计。
  • 你至少应该说出你想用这段代码实现的什么。 @Str1 是什么?代码不清楚。
  • 核心问题是我基本上需要从经典的 ASP Web 应用程序将数据表放入存储过程中。我离成为数据库专家还差得很远,所以首先想到的是获取所有分隔字符串,将它们传递给 SP,将它们拆分,然后将它们重新组合到一个表中以供使用。我将是第一个承认这看起来比所有人都丑陋的人,如果有更好的方法来处理这个问题,我很乐意接受建议。至于@Str1是什么,只是传给SP的字符串变量。

标签: sql performance tsql join


【解决方案1】:

从性能角度来看,这确实不是一个好策略。用户定义的函数永远不会是高性能的。您是否考虑过将数据作为 XML 文档提交并在存储过程中进行解析?我以前做过,而且通常表现不错。

关于您的实际问题,可能有一些功能可以优化您的表值函数,以便它返回一个定义了主键的表变量,这可以提高性能,但实际上,我建议您更改上传策略。

【讨论】:

  • 套用JWZ,我认为使用XML 只会给您带来两个问题。 OP 已经有可以通过编程方式分解的字符串。
  • XML 在 SQL Server 中定义明确、灵活且本机可解析。 OP 正在推出自己的产品,而不是使用内置功能。我一直在使用这种东西,而且效果很好……而且是人类可读的。不要认为有关 Regex 的引用适用。
  • 是的,XML 可能是要走的路,那么我首先要避免所有丑陋的连接。感谢您的建议!
【解决方案2】:

对于 30 个表变量,连接将非常低效,因为它必须为每个连接扫描每个变量。您需要使其行为更像 O(N) 或 O(N log N) 的操作 - 实际上,我认为这意味着对所有输出进行排序并识别为由查询优化器排序。我想不出一种简单、干净和紧凑的方法。但是,以下方法之一可能有效:

  • 将每个拆分函数的输出加载到一组临时表中,并为每个表的 ID 创建一个聚集索引。这可能会得到一个相当有效的合并连接计划,并且聚集索引构建将是 O(N log N) 和有效的 O(N) 合并连接操作。

  • 在每个表上打开按 ID 排序的游标并按程序循环遍历它们(即,每次循环迭代从每个游标中获取一行)。如果函数输出的行数相当小,那么这可能不会太昂贵。游标操作会有些昂贵,但查询实际上是 O(N log N),循环操作是 O(N)。

一个更好的计划可能是按程序完成整个事情(即避免使用表值拆分函数)。如果字符串在你得到它们时都可以按 ID 排序(或者可能作为初步步骤),你可以用字符串操作来完成整个事情。从每个字符串中剥离第一条记录并存储字符串的尾部以供下一次迭代。

【讨论】:

  • 表变量也可以在 id 上有聚集索引。 declare @t table(id int primary key)
猜你喜欢
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
  • 2020-04-07
  • 1970-01-01
相关资源
最近更新 更多