TSQL游标如何检查是否已经声明并因此释放

发布于 2024-12-24 19:52:13 字数 1016 浏览 0 评论 0 原文

在尝试再次打开游标之前,如果游标已存在,如何确保将其释放?

对于表,我可以使用类似的东西:

if exists (select top 1 from tempdb.sys.tables where name = '##tmpTable') 
    drop table ##tmpTable;  
... then I can recreate my ##tmpTable 

但我无法弄清楚如何为光标执行此操作,就像

-- First clean up if already exists..
  .....                                  <----- what goes here???

-- Declare and use a cursor
DECLARE  someCursorName CURSOR 
 FOR 
  select something from somewhere
 FOR READ ONLY

我这样做是为了确保我的脚本在开始工作之前清理干净

我能想到的最好的是:

begin try DEALLOCATE someCursorName ; end try begin catch end catch

是这样的一个好的做法?

编辑: 这是维护脚本。在我们大量客户定制的数据库中,可能有许多表,游标用于跨表运行统计分析 - 根据表的类型,会发生不同的情况。基本上有很多动态sql。如果脚本失败,我希望能够重复该工作,而不必担心手动干预。这里只有一层范围。

像所有事情一样,我很高兴用设置操作替换光标。这些是游标循环所做的事情:

  • 构造sql来重组/重建索引(最初有手动sql来确定要运行的DDL,然后发出DDL)
  • 分析数据分布和错误不同的表
  • 在日志中查找错误并查找适当的表并获取该数据(最初有手动sql来确定错误所在的位置,然后剪切并粘贴模板以根据错误类型查找错误)

How can I make sure that I deallocate a cursor if it already exists before I try and open it again?

For a table I can use something like:

if exists (select top 1 from tempdb.sys.tables where name = '##tmpTable') 
    drop table ##tmpTable;  
... then I can recreate my ##tmpTable 

But I can't work out how to do it for a cursor like

-- First clean up if already exists..
  .....                                  <----- what goes here???

-- Declare and use a cursor
DECLARE  someCursorName CURSOR 
 FOR 
  select something from somewhere
 FOR READ ONLY

I'm doing this to ensure that my script cleans up before it starts work

Best I can come up with is :

begin try DEALLOCATE someCursorName ; end try begin catch end catch

Is this a good practice?

EDIT:
This is maintennance script. In our heavily customer customised databases there can be many tables and the cursor is used to run statistical analyses across the tables - depending on the types of tables different things happen. Basically lots of dynamic sql. If the script fails I'd like to be able to repeat the job without worrying about manual intervention. There is only one level of scope here.

Like all things I'm happy to replace the cursors with set operations. These are the things that the cursors loops do:

  • construct sql to reorg/rebuild indexes (orginally there was manual sql to determine the DDL to run, and then the DDL was issued)
  • analyse data spreads and errors in different tables
  • find errors in logs and look up appropriate tables and grab that data (orginally there was manual sql to determine the places where errors where and then cut and paste template(s) to look up the errors dependant upon types of error)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

漫漫岁月 2024-12-31 19:52:13

您可以将游标声明为变量,然后当它超出范围时它将自动关闭并释放。下面是将其与动态 SQL 结合使用的示例。

DECLARE @C1 AS CURSOR;

SET @C1 = CURSOR FAST_FORWARD
FOR SELECT name
    FROM   master..spt_values
    WHERE  name <> ''
    ORDER  BY name;

OPEN @C1;

EXEC sp_executesql N'
DECLARE @name VARCHAR(50)
FETCH NEXT FROM @C1 INTO @name;

WHILE @@FETCH_STATUS = 0
  BEGIN
      PRINT @name

      FETCH NEXT FROM @C1 INTO @name;
  END 
', N'@C1 CURSOR', @C1 = @C1

You can declare the Cursor as a variable then it will be closed and deallocated automatically when it goes out of scope. Example of using this in conjunction with dynamic SQL below.

DECLARE @C1 AS CURSOR;

SET @C1 = CURSOR FAST_FORWARD
FOR SELECT name
    FROM   master..spt_values
    WHERE  name <> ''
    ORDER  BY name;

OPEN @C1;

EXEC sp_executesql N'
DECLARE @name VARCHAR(50)
FETCH NEXT FROM @C1 INTO @name;

WHILE @@FETCH_STATUS = 0
  BEGIN
      PRINT @name

      FETCH NEXT FROM @C1 INTO @name;
  END 
', N'@C1 CURSOR', @C1 = @C1
沙沙粒小 2024-12-31 19:52:13

创建 CURSOR 变量,然后使用 SET 定义它时,当变量超出范围(即批处理或子流程结束)时,确实会自动释放它,这种构造也是完全没有必要的。

假设您没有将游标本身传递到进程的各个嵌套级别,或者需要它在批次之间生存,那么最简单的方法(可能也是最好的总体方法)是将游标声明为 LOCAL。虽然可以在 LOCALGLOBAL 之间配置未指定,但如果未指定,则默认设置为游标为 GLOBAL。在问题的代码中,没有 LOCAL 关键字,因此我们可以假设光标是 GLOBAL 这就是为什么需要清理先前运行的问题甚至出现的原因向上。因此,只需将 LOCAL 关键字添加到游标声明中即可。

现在,对于那些您实际上想要一个GLOBAL游标并因此需要首先检查以确保它尚未被声明的情况,有两种简单的方法可以对此进行测试:

  1. sys.dm_exec_cursors DMF(类似于对表使用 sys.tables
  2. 使用 CURSOR_STATUS 函数可以告诉你它是否存在,以及如果是,则它是打开还是关闭,如果打开,则是否有 0 行或多于 0 行。

我还在 SQL Server 2005 SP4 上进行了测试,发现上述所有三项的行为方式也相同。

While creating a CURSOR variable and then using SET to define it will indeed automatically deallocate it when the variable goes out of scope (i.e. end of the batch or sub-process), that construct is also entirely unnecessary.

Assuming that you aren't passing the cursor itself around to various nested levels of the process, or need it to survive between batches, then the simplest approach (and probably best overall) is to declare the cursor as LOCAL. While unspecified is configurable between LOCAL and GLOBAL, the default setting is that cursors are GLOBAL if not specified. In the code in the question, there is no LOCAL keyword so we can assume that the cursor is GLOBAL which is why this issue of needing to clean up a prior run even came up. So, just add the LOCAL keyword to the cursor declaration.

Now, for those cases where you actually want a GLOBAL cursor and so need to check first to make sure that it hasn't already been declared, there are two easy ways to test for this:

  1. select from the sys.dm_exec_cursors DMF (similar to using sys.tables for tables)
  2. use the CURSOR_STATUS function which can tell you if it exists, and if so, if it's open or closed, and if open, then if there are 0 or more than 0 rows.

I also tested on SQL Server 2005, SP4 and found that all three items noted above behaved the same way there as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文