【问题标题】:T-SQL Cursor to update several columns in several tablesT-SQL 游标更新多个表中的多个列
【发布时间】:2015-09-10 12:52:14
【问题描述】:

我正在与一位客户合作,他以某种方式将小写项目编号加载到一系列 SQL 表中。这在这个特定的应用程序中是不可能的,并且会导致各种问题。我打算一次将这些项目更新为大写版本,但很快意识到这将花费很长时间。所以我做了一件不可思议的事,并试图用光标来帮助我,但是我一直被一个让我困惑的错误绊倒。这是我的代码:

declare @tablename varchar(10)

declare upper_case cursor for 
    SELECT sys.objects.NAME
    FROM sys.columns
    INNER JOIN sys.objects ON sys.columns.object_id = sys.objects.object_id
    WHERE sys.columns.NAME = 'item'
      AND sys.objects.type = 'U'

OPEN upper_case
FETCH NEXT FROM upper_case into @tablename

UPDATE @tablename
SET item = upper(item)

CLOSE upper_case
DEALLOCATE upper_case

这是错误:

消息 1087,第 16 级,状态 1,第 13 行
必须声明表变量“@tablename”。

我没有将@tablename 用作表变量,而是尝试将其用作标量变量,但我想,到底怎么回事,我会咬一口的。所以我把它切换到一个表变量:

declare @tablename table (tablename varchar(10))

然后我得到这个错误:

消息 137,第 16 级,状态 1,第 5 行
必须声明标量变量“@tablename”。

我错过了什么?我是否不允许在UPDATE 语句中使用变量?我知道每个UPDATE 只能更新一个表,但我认为通过使用游标,我实际上是在发出多个更新,每个更新只更新一个表。我误会了吗?

这是有效的结果。我不敢相信我是第一个想要这样做的人,我怀疑我会是最后一个:

DECLARE @tablename varchar(10)
DECLARE @sql nvarchar(max)

declare upper_case cursor for 
    SELECT sys.objects.NAME
    FROM sys.columns
    INNER JOIN sys.objects ON sys.columns.object_id = sys.objects.object_id
    WHERE sys.columns.NAME = 'item'
      AND sys.objects.type = 'U'

OPEN upper_case
FETCH upper_case into @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = 'UPDATE [' + @tablename + '] SET item = upper(item)'
    EXEC(@sql)

    FETCH upper_case into @tablename
END

CLOSE upper_case
DEALLOCATE upper_case

【问题讨论】:

  • 您缺少动态 SQL,快速解决方法EXEC('UPDATE ' + @tablename + ' SET item = upper(item))'

标签: sql-server tsql cursor sql-update


【解决方案1】:

UPDATE 你不能像你尝试的那样将表名作为变量传递:

UPDATE @tablename
SET item = upper(item);

Msg 1087, Level 16, State 1, Line 13 必须声明表变量 “@表名”

但是UPDATE 可以使用表变量DECLARE @t TABLE = ...(不是你的情况)。

要以你想要的方式使用它,你需要像这样的动态 SQL:

EXEC('UPDATE [' + @tablename + '] SET item = UPPER(item)');

【讨论】:

  • 完美。太感谢了。我没有意识到更新不允许变量名。我的光标也有问题,但我也解决了这个问题。我在上面发布了修改后的代码
【解决方案2】:

正如 lad2025 指出的那样,您需要使用动态 SQL 技术来完成您想要做的事情。另外两点:

首先,您只是更新光标返回的第一个结果。您需要使用while 循环来遍历游标结果,如下所示:

....
FETCH NEXT FROM upper_case into @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
    <do dynamic update>

    FETCH NEXT FROM upper_case into @tablename
END
<close & deallocate>

其次,我真的鼓励使用INFORMATION_SCHEMA 视图而不是直接查询系统表。出于您的目的,它们提供了足够多的信息,并且跨 SQL 版本启动时更具可读性和稳定性。

INFORMATION_SCHEMA 中的系统元数据是 SQL-92 标准。它在 MS-SQL 版本和其他符合 ANSI-SQL 的引擎中都很稳定。另请参阅:INFORMATION_SCHEMA vs sysobjects。编写快速脚本的主要缺点是您必须键入“information_schema”而不仅仅是“sys”。最终肌肉记忆开始发挥作用:)

【讨论】:

  • 谢谢马克。我对游标相当陌生,因为有人告诉我要避免使用它们,所以这也是一种学习体验。为什么建议使用INFORMATION_SCHEMA 而不是 sys.objects?我已经被警告过很多关于游标的信息,但这是我第一次听说系统表。
  • 已编辑以充实回复。您是对的,通常要避免使用游标,但是对于像您这样的动态 SQL,它们是必不可少的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-22
  • 2019-05-31
  • 2012-02-23
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 2021-05-13
相关资源
最近更新 更多