使用游标时从游标中删除一行

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,但是,真正的问题是为什么您首先要使用CURSORCURSOR 是一个迭代任务,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 游标。 哦,好的,我明白了。非常感谢!

以上是关于使用游标时从游标中删除一行的主要内容,如果未能解决你的问题,请参考以下文章

sqlserver中怎样使用游标for循环

sqlserver中怎样使用游标for循环

SQL中游标的使用(转)

如何从游标中获取、删除、提交

数据库原理与应用(SQL Server)笔记 第十一章 游标

使用游标