【问题标题】:Using a SplitString Table Values Function in an Inner Join在内部联接中使用 SplitString 表值函数
【发布时间】:2021-11-30 04:55:57
【问题描述】:

我有一个表 ProcessQueueLog,其中包含一个分隔字段值,我需要将其拆分并连接到另一个表。我有一份工作声明,但似乎相当缓慢。有人可以建议更好的方法吗?

fn_SplitString 是由一位早已离开的开发人员在内部编写的。它返回 part1 和 part2 并且是任何拆分字符串函数的典型。

SELECT pql.*, cl.ShortNote
FROM Automation.ProcessQueueLog pql
    INNER JOIN dbo.CLog         cl
        ON cl.Conversation_ID = CAST((SELECT Part1 FROM dbo.fn_SplitString (pql.QueuedRecordId, '-')) AS INT)
           AND cl.Memo_ID = CAST((SELECT Part2 FROM dbo.fn_SplitString (pql.QueuedRecordId, '-')) AS INT);

【问题讨论】:

  • 最好不要在数据库中存储连字符分隔的数据。我不确定如何改进。
  • 如果你有拥抱数据,最好创建另一个字段并将记录保存在不带-(连字符)的新字段中。
  • 假设您的函数返回多列,在 CROSS APPLY 中运行一次并在结果上执行 JOIN
  • 什么版本的 SQL Server? fn_SplitString 是多语句表值函数吗?你能分享一下吗?可能它可以被改进或只是消除(拆分固定数量的子字符串没有多大价值,比如 2)。

标签: sql sql-server tsql


【解决方案1】:

扩展我的评论

SELECT pql.*
     , cl.ShortNote
FROM Automation.ProcessQueueLog pql
CROSS APPLY dbo.fn_SplitString(pql.QueuedRecordId, '-') SS
INNER JOIN dbo.CLog         cl
        ON cl.Conversation_ID = SS.Part1
       AND cl.Memo_ID = SS.Part2;

我怀疑你的拆分函数有一个循环。如果是这样,还有更多的性能方法

【讨论】:

  • 谢谢,@John。这比原来的性能要差一些。一个 705 记录集用了 31 秒,而原来的这个例子用了 57 秒。编辑:公平地说,dbo.clog 实际上是一个链接服务器连接。我在 SO 上为这个脚本简化了它,所以这也可能导致执行缓慢。
  • 很难想象。一个电话对两个。
【解决方案2】:

另一种消除(几乎可以肯定是低效的)函数的方法,如果我们只从字符串中破坏少量、已知和固定数量的元素(拆分函数,即使是非常低效的函数,也是最有用的当输入元素的数量未知时):

;WITH ql(q,p) AS 
(
  SELECT QueuedRecordId, CHARINDEX('-', QueuedRecordId) -- , other cols
  FROM Automation.ProcessQueueLog
  WHERE QueuedRecordId LIKE '%[0-9]-[0-9]%'
)
SELECT ql.*, cl.ShortNote
FROM ql CROSS APPLY 
(
  VALUES
  (
    TRY_CONVERT(int,LEFT(q,p-1)), 
    TRY_CONVERT(int,SUBSTRING(q,p+1,32))
  )
) AS q2(l,r)
INNER JOIN dbo.CLog AS cl
ON cl.Conversation_ID = q2.l
AND cl.Memo_ID = q2.r;

为了保证与缺少 - 的无效值隔离,这些值在 CTE 内部的过滤器中转义,您只需将 q2 中的第一行(产生 l 的表达式,“左侧”)更改为稍微更丑:

    TRY_CONVERT(int,LEFT(q,COALESCE(NULLIF(p,0),1)-1)), 

(尽管该修复假定 0 不能代表有效的 ID。)

如果您使用的是 SQL Server 2016 或更高版本(对于指定您需要支持的版本总是有用的),另一种可能性是使用 OPENJSON 进行可靠、有序、基于集合的拆分,然后使用 PIVOT 将其包装回去:

;WITH ql AS
(
  SELECT ql.QueuedRecordId, j.[key], j.[value]
  FROM Automation.ProcessQueueLog AS ql CROSS APPLY 
  OPENJSON ('["' + REPLACE(ql.QueuedRecordId,'-','","') + '"]"') AS j
)
SELECT p.QueuedRecordId, cl.ShortNote 
FROM ql PIVOT (MAX([value]) FOR [key] IN ([0],[1])) AS p
INNER JOIN dbo.CLog AS cl
ON cl.Conversation_ID = p.[0]
AND cl.Memo_ID = p.[1];

但是,如果任何缓慢是由于您忘记提及的链接服务器连接(怎么可能不是?),您可能会追尾。

我的建议:使用CLog 表的本地 副本(实际发布,发布-执行计划到PasteThePlan.com,您认为应该有效但不是),然后单独对网络连接进行故障排除。据我们所知,缓慢可能是从明显的索引丢失到 can-and-string 网络连接。

【讨论】:

  • 是的,你是绝对正确的。我在完整脚本上实现了您的解决方案,执行时间为 44 秒。无论如何,我对自己的做法并不满意,你们向我展示了几种不同的选择,这些选择要好得多,这很有帮助。我认为我的减速是在其他地方,而不是在我原来的加入中。我最初是在做一个 SubString/Left/length 类型的操作来解析它,但是我发现当有一个没有分隔符的值时我得到了一个错误。因此,沿着兔子洞旅行:)
猜你喜欢
  • 2013-12-05
  • 2014-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 2017-01-17
  • 2011-01-24
相关资源
最近更新 更多