使用游标时从游标中删除一行
Posted
技术标签:
【中文标题】使用游标时从游标中删除一行【英文标题】:Delete a row from cursor while using the cursor 【发布时间】:2020-08-12 16:08:07 【问题描述】:我有一个遍历我的临时表的游标。在迭代时,我想检查一个条件并根据条件删除一些行(我将删除迭代器尚未到达的行)。
我尝试从光标正在迭代的表中删除行(所以临时表),但没有成功,我可以在Messages panel
中看到它们(我打印它的名称)。
是否可以从表中删除游标在 SQL-Server 中迭代的行?如果不是,我的替代方案是什么?
基本上,临时表包含树状数据,根据列的值,如果它不符合条件,我需要删除它的子项(和孙子等)。
DECLARE cursor_name CURSOR
FOR (SELECT * FROM #test) ORDER BY Path
DECLARE
@Id AS INTEGER,
@Name AS VARCHAR(MAX),
@Path AS VARCHAR(MAX)
OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO @Id, @Name, @Path;
PRINT @Name
DELETE FROM #test
WHERE
Path LIKE '%76939%'
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM cursor_name INTO @Id, @Name, @Path;
PRINT @Name
END;
CLOSE cursor_name;
DEALLOCATE cursor_name;
#编辑
这里有更多关于这个问题的细节。我们的数据结构类似于树列表。每个项目都有多个列,这些列指定了该行的某些特征。这些特征可以被继承也可以不被继承(如果InheritanceFlag
为1,那么它被继承,如果它是0,那么它不是)。
因此,当用户进行更改时,我们需要将更改传播给其子项,具体取决于所述标志。如果它的一个孩子将InheritanceFlag
设置为0,那么它不会改变它的值,它的孩子也不会。我想使用路径删除那些带有光标的行。
这是我拥有的数据。 ParentID
是其父代的 ID。在这种情况下,假设我们正在编辑项目 76938,因此我们正在查看它的子项。 ToEdit
列是我要创建的;有了它,我可以过滤行并直接将特征列更改为新值。
+-------+----------+-------+-------------------------+-----------------+--------+
| ID | ParentID | Name | Path | InheritanceFlag | ToEdit |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76938 | NULL | 1 | (76938) | 1 | X |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76942 | 76938 | 1.1 | (76938)\(76942) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76952 | 76942 | 1.1.1 | (76938)\(76942)\(76952) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76961 | 76942 | 1.1.2 | (76938)\(76942)\(76961) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76943 | 76938 | 1.2 | (76938)\(76943) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76944 | 76938 | 1.3 | (76938)\(76944) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76946 | 76944 | 1.3.1 | (76938)\(76944)\(76946) | 1 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76947 | 76944 | 1.3.2 | (76938)\(76944)\(76947) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76948 | 76944 | 1.3.3 | (76938)\(76944)\(76948) | 1 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76945 | 76938 | 1.4 | (76938)\(76945) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
【问题讨论】:
您可以在CURSOR
中使用DELETE
,但是,真正的问题是为什么您首先要使用CURSOR
。 CURSOR
是一个迭代任务,SQL Server(和其他 RDBMS)在此类任务中表现不佳。 RDBMS 旨在完成基于集合的任务。上述的真正目标是什么,为什么不使用基于集合的方法?
这有点难以解释,但我会试一试。我们的数据结构类似于树列表。有一个特性(多列)可以从父级继承或不继承。我们有一个 INT 标志告诉我们(每一位给出一列的继承设置)。所以我需要传播一个(或多个)特征的更改,并且我正在使用游标,所以当我看到一行没有从其父级继承时,我需要删除它的所有子级(我们有一个路径列,所以一个简单的WHERE path like ...
就足够了。我真的不明白在这种情况下如何不使用光标。
我认为你需要一个动态光标。
一个 rCTE 可以很好地满足您的需求。样本数据和预期结果将极大地帮助我们为您提供帮助。
好吧,等一下,我会建立一些东西
【参考方案1】:
如果游标是 DYNAMIC,您可以从基础表中删除并从未来的 FETCH 中删除行,并且定义游标的查询不需要假脱机,从而有效地将其变成 STATIC 游标。
在您的代码中,按未索引的 VARCHAR(MAX) 排序可防止游标看到基础表中的任何更改。
比如这个
drop table if exists #test
go
create table #test(id integer, name varchar(max), path varchar(1000), index ix_path (path))
insert into #test(id,name,path) values (1,'a','0000000'),(2,'b', '0769391'),(3,'c', '1768391')
DECLARE cursor_name CURSOR DYNAMIC
FOR SELECT * FROM #test ORDER BY path
DECLARE
@Id AS INTEGER,
@Name AS VARCHAR(MAX),
@Path AS VARCHAR(MAX)
OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO @Id, @Name, @Path;
PRINT @Name
print 'deleting'
DELETE FROM #test
WHERE
Path LIKE '%76939%'
WHILE 1=1
BEGIN
FETCH NEXT FROM cursor_name INTO @Id, @Name, @Path;
if @@FETCH_STATUS <> 0 break
PRINT @Name
END;
CLOSE cursor_name;
DEALLOCATE cursor_name;
输出
(3 rows affected)
a
deleting
(1 row affected)
c
【讨论】:
我在path
上添加了索引并且它有效,但我不知道为什么。需要解释一下吗?
当您为无法创建的查询请求动态游标时,您将获得静态游标。运行select * from sys.dm_exec_cursors(@@spid)
以查看实际创建的游标类型。 path
上的索引允许 SQL 构建一个查询计划,该计划按 path
就地排序行,因此可以使用 DYNAMIC 游标。
哦,好的,我明白了。非常感谢!以上是关于使用游标时从游标中删除一行的主要内容,如果未能解决你的问题,请参考以下文章