递归变量自赋值有效吗?

Ian*_*oyd 1 t-sql sql-server recursive-query

我发现了一些 T-SQL 代码,我试图弄清楚它是否是:

\n
    \n
  • 一个我从来不知道的优雅的解决方案
  • \n
  • 一个可怕的令人厌恶的东西碰巧起作用了,绝对应该被移除
  • \n
  • 或介于两者之间。
  • \n
\n

标准化名称

\n

我们有一个要求尝试清理名称:

\n
    \n
  • Bob\xe2\x86\x92Robert
  • \n
  • Bill\xe2\x86\x92William
  • \n
  • Dick\xe2\x86\x92Richard
  • \n
\n

因此创建了一个表来保存该以及将其替换为的值。它包含上述单词(以及更深奥的、特定领域的、特定行业的映射):

\n

填充词

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n
价值用。。。来代替
迪克理查德
账单威廉
鲍勃罗伯特
特德威廉
\n
\n

我看到的用于执行替换的代码要么是天才,要么是疯狂:

\n
DECLARE @firstName varchar(200) = \'bill\';\nSELECT @firstName = REPLACE(@firstName, FillerWords.Value, FillerWords.ReplaceWith) FROM FillerWords;\nSELECT @firstName;\n
Run Code Online (Sandbox Code Playgroud)\n

这使:

\n
(no column name)\n----------------\nWilliam\n\n1 row(s) affected\n
Run Code Online (Sandbox Code Playgroud)\n

更令人印象深刻的是它可以一次进行多个替换:

\n
DECLARE @firstName varchar(200) = \'teddickbobbill\';\nSELECT @firstName = REPLACE(@firstName, FillerWords.Value, FillerWords.ReplaceWith) FROM FillerWords;\nSELECT @firstName;\n
Run Code Online (Sandbox Code Playgroud)\n

这使:

\n
(no column name)\n----------------\nWilliamRichardRobertWilliam\n\n1 row(s) affected\n
Run Code Online (Sandbox Code Playgroud)\n

它有效,但有效吗?

\n

它是如何工作的?

\n

如果在选择过程中消除变量赋值:

\n
SELECT REPLACE(@firstName, fw.Value, fw.ReplaceWith) FROM #FillerWords fw\n
Run Code Online (Sandbox Code Playgroud)\n

您会看到它正在对REPLACE表中的每一行执行:

\n
(No column name)\nteddickRobertbill\ntedRichardbobbill\nWilliamdickbobbill\nteddickbobWilliam\n
Run Code Online (Sandbox Code Playgroud)\n

因此,每次计算一行时,@firstName都会更新变量:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n
@名价值用。。。来代替@firstName 的新值
泰迪克鲍比尔迪克理查德特德Richard·鲍比尔
特德·理查德·博比尔账单威廉特德·理查德·鲍勃William
泰德·理查德·鲍勃·威廉鲍勃罗伯特特德·理查德Robert·威廉
特德·理查德·罗伯特·威廉特德威廉William理查德·罗伯特·威廉
\n
\n

因此,它似乎依赖于变量赋值的一个怪癖,即您可以不断地逐行修改相同的变量;这几乎就像一个光标操作。

\n

是否支持这种 T-SQL 编程风格?我知道递归公用表表达式(CTE)一直在这样做;但这里允许吗?

\n

我知道代码的成功取决于处理行的顺序,这在 SQL 中通常无法保证。这可能会导致结果不一致,尤其是当基础数据或 SQL Server 的查询执行计划发生更改时。在这个问题中,我不关心这个。

\n

一个可能的答案

\n

我问了一位分析基数的神谕机制,它说:

\n
\n

该代码利用了 T-SQL 的特定行为,其中可以在 SELECT 语句中更新变量。然而,这不是标准的 SQL 行为,并且可能不直观或不可维护。虽然这在当前版本的 SQL Server 中有效,但不能保证此类行为在未来版本中保持一致或受支持,因为它不是用于此类目的的记录功能。

\n
\n

递归设置变量不是可接受的模式吗?

\n

Mar*_*ith 8

这与旧的字符串聚合技术属于同一类,旧的字符串聚合技术有时有效,有时无效(取决于执行计划),并且从未成为受支持的技术。

该文档明确警告“反模式使用递归变量赋值存档

你的代码:

SELECT @firstName = REPLACE(@firstName, ...) 
FROM
Run Code Online (Sandbox Code Playgroud)

当然是对结构的警告:

SELECT @Var = <expression containing @Var>
FROM
Run Code Online (Sandbox Code Playgroud)

绝对不应该出现在您关心的生产代码中,因为它不能保证工作。

在字符串连接方法“失败”的情况下,变量的结果值仅包含单个赋值的结果,因此我预计有可能获得一个仅有效执行单个赋值的计划REPLACE

  • 即:https://dbfiddle.uk/P8QtYymi (2认同)