如何使用 while 循环更新行?
Posted
技术标签:
【中文标题】如何使用 while 循环更新行?【英文标题】:How do I update rows using while loop? 【发布时间】:2021-12-28 14:16:01 【问题描述】:我正在尝试在 while 循环中更新更新查询所在的 SQL 行。
DECLARE @Sayac INT
SET @Sayac= (select count(ABBREVIATION) from [MSTR_MD].[dbo].[v_OpcoGeneral_UserList])
WHILE (@Sayac>0)
BEGIN
PRINT @Sayac;
(select ABBREVIATION FROM [MSTR_MD].[dbo].[v_OpcoGeneral_UserList] WHERE [MSTR_MD].[dbo].[v_OpcoGeneral_UserList].ID = @Sayac)
UPDATE [dwh].[dbo].[opco_securty]
SET opco_general = REPLACE(opco_general, (select ABBREVIATION FROM [MSTR_MD].[dbo].[v_OpcoGeneral_UserList] WHERE [MSTR_MD].[dbo].[v_OpcoGeneral_UserList].ID = @Sayac), '')
SET @Sayac = @Sayac - 1
END
但是在这个查询中,它没有按预期工作。我猜 while 循环不像编程语言那样被编译。你能推荐一个解决方案吗?谢谢!
例如,如果从视图中接收到“Ugurcan.Kaya”,则从 opco_securty 表的 opco_general(文本)列中删除“Ugurcan.Kaya”。
【问题讨论】:
预期的逻辑看起来很奇怪。为什么要将计数与 id 列进行比较?这感觉像是一个错误。你能澄清问题中的要求吗? 我正在接收来自 [MSTR_MD].[dbo].[v_OpcoGeneral_UserList] 的用户 ID,并希望替换 opco_security 表中检索到的用户 ID 所在的文本。 基本上例如,如果从视图中接收到“Ugurcan.Kaya”,则从 opco_securty 表的 opco_general 列中删除“Ugurcan.Kaya”。 请用最小的可重现测试用例更新问题。但是您正在使用 COUNT 与用户 ID 进行比较。这似乎是一个错误。 您最好向我们展示您在这里实际想要实现的目标。 【参考方案1】:我认为你可以在没有循环的情况下使用递归 CTE 做你想做的事情。我从这篇文章中借用了这个想法Multiple replacements in string in single Update Statement in SQL server 2005
我对其进行了安排,以便我相信它适用于您的用例。第一个 CTE 使用递归继续应用替换,直到不再存在匹配。
第二个 CTE 用于标识每个发生的递归替换的 row_number。我们感兴趣的 row_number 是 securty
表中每个字符串的最高 row_number,因为它将包含所有必需的替换。
最后,我们运行一条更新语句,用适当的替换字符串更新securty
表中的每条记录。
这是我编写的用于完成这项工作的代码,带有示例数据和表格:
CREATE TABLE UserList(
id int,
abbr varchar(25)
);
CREATE TABLE securty(
id int,
genrl varchar(250)
);
INSERT INTO UserList(id, abbr)
VALUES(1, 'Ugurcan.Kaya');
INSERT INTO UserList(id, abbr)
VALUES(2, 'User5');
INSERT INTO UserList(id, abbr)
VALUES(3, 'User6');
INSERT INTO securty(id, genrl)
VALUES(1, 'User1-User2-Ugurcan.Kaya-Lorum-User5-User16-User17');
INSERT INTO securty(id, genrl)
VALUES(2, 'User5-Ugurcan.Kaya-Lorum-User5-User16-User17');
;With Replacements as (
SELECT
s.id, s.genrl, 0 AS repcount
FROM
securty AS s
UNION ALL
SELECT
rs.id, CAST(REPLACE(genrl, ul.abbr, '') AS VARCHAR(250)),rs.repcount+1
FROM
Replacements rs
JOIN
UserList ul ON CHARINDEX(ul.abbr,rs.genrl) > 0
),
Final_Replacements AS (
SELECT
rs.id, rs.genrl, ROW_NUMBER() OVER (PARTITION BY rs.id ORDER BY rs.repcount DESC) AS rn
FROM
replacements AS rs
)
UPDATE s
SET s.genrl = fs.genrl
FROM securty AS s
JOIN Final_Replacements AS fs
ON fs.id = s.id AND fs.rn = 1
我还创建了一个 sqlfiddle 供您试用:http://sqlfiddle.com/#!18/904e0/1/0
【讨论】:
【参考方案2】:The fiddle
也许这将有助于理解在UPDATE
语句中将一组替换模式应用于目标表的一种方法。
澄清一下,REPLACE
将替换字符串中所有匹配的匹配项,如上一个示例字符串中所示...'6this is XYZ another XYZ test'
已更新为 '6this is another test'
。
来自 t-sql 文档:
替换 (Transact-SQL)
用另一个字符串值替换所有出现的指定字符串值。
SQL:
UPDATE test
SET test.data = REPLACE(data, pattern, '')
FROM test
JOIN abbrev
ON test.data LIKE CONCAT('%', pattern, '%')
;
设置:
CREATE TABLE test ( id int, data varchar(40) );
INSERT INTO test VALUES
( 1, '1this is a NameToRemove test' )
, ( 2, '2this is a NameToNotRemove test' )
, ( 3, '3this is another NameXToRemove test' )
, ( 4, '4this is another NameXToRemove test' )
, ( 5, '5this is another NameXToRemove test' )
, ( 6, '6this is XYZ another XYZ test' )
;
CREATE TABLE abbrev ( id int, pattern varchar(20) );
INSERT INTO abbrev VALUES
( 1, 'NameToRemove' )
, ( 2, 'NameXToRemove' )
, ( 3, 'NameZToRemove' )
, ( 4, 'XYZ' )
;
数据:
id | data |
---|---|
1 | 1this is a NameToRemove test |
2 | 2this is a NameToNotRemove test |
3 | 3this is another NameXToRemove test |
4 | 4this is another NameXToRemove test |
5 | 5this is another NameXToRemove test |
6 | 6this is XYZ another XYZ test |
结果:
id | data |
---|---|
1 | 1this is a test |
2 | 2this is a NameToNotRemove test |
3 | 3this is another test |
4 | 4this is another test |
5 | 5this is another test |
6 | 6this is another test |
【讨论】:
这确实假设一行不能有多个缩写。 没错。这只是一个例子。如果问题数据中未发现其他要求,则可以使用其他略有不同的方法。 @Larnu 实际上,REPLACE 确实处理替换字符串中所有匹配的模式,至少在某些情况下是这样。我会更新测试用例。 但是会出现多个不同替换的问题。 db<>fiddle 显示多个匹配项会发生什么。以上是关于如何使用 while 循环更新行?的主要内容,如果未能解决你的问题,请参考以下文章