3

I'm working with a customer who somehow loaded lowercase item numbers into a series of SQL tables. This should not be possible in this particular application and is causing all sorts of issues. I set out to update the items to the upper case versions one at a time but quickly realized that this would take forever. So I did the unthinkable and tried to use a cursor to help me out, however I keep stumbling over an error that has me baffled. Here's my code:

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

And here's the error:

Msg 1087, Level 16, State 1, Line 13
Must declare the table variable "@tablename".

I'm not using @tablename as a table variable, I'm trying to use it as a scalar variable but I thought, what the heck, I'll bite. So I switched it to a table variable:

declare @tablename table (tablename varchar(10))

And then I get this error:

Msg 137, Level 16, State 1, Line 5
Must declare the scalar variable "@tablename".

What am I missing? Am I not allowed to use a variable in an UPDATE statement? I know that each UPDATE can only update one table, but I thought that by using the cursor, I was effectively issuing multiple updates, each which only updates one table. Did I misunderstand that?

Here's the result that worked. I can't believe I'm the first to want to do this and I doubt I'll be the last:

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
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

2 Answers2

3

UPDATE you cannot pass table name as variable like you try:

UPDATE @tablename
SET item = upper(item);

Msg 1087, Level 16, State 1, Line 13 Must declare the table variable "@tablename"

But UPDATE can work with table variables DECLARE @t TABLE = ... (not your case).

To use it how you want you need Dynamic-SQL like:

EXEC('UPDATE [' + @tablename + '] SET item = UPPER(item)');
Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
  • 1
    perfect. thank you so much. I didn't realize that update does not allow variable names. I also had a problem with my cursor but I fixed that too. I posted the amended code above – 3xStampA2xStamp Sep 10 '15 at 03:29
2

As lad2025 points out, you need to use Dynamic SQL techniques to accomplish what you're trying to do. Two other points:

First, you're only updating the first result your cursor returns. You need to use a while loop to iterate through the cursor results, like so:

....
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>

Second, I would really encourage use of the INFORMATION_SCHEMA views rather than directly querying system tables. For your purposes they provide more than enough information, and are more readable and stable across SQL versions to boot.

The system metadata in INFORMATION_SCHEMA is a SQL-92 standard. It's stable across MS-SQL version and other ANSI-SQL-compliant engines. See also: INFORMATION_SCHEMA vs sysobjects. The main downside for writing quick scripts is that you have to type "information_schema" instead of just "sys". Eventually muscle-memory kicks in :)

Community
  • 1
  • 1
Marc L.
  • 3,296
  • 1
  • 32
  • 42
  • Thanks Marc. I'm fairly new to cursors as I've been told to avoid them so that was a bit of a learning experience as well. Why do you recommend using `INFORMATION_SCHEMA` instead of sys.objects? I've been warned plenty about cursors but this is the first I've heard about the system tables. – 3xStampA2xStamp Sep 10 '15 at 03:33
  • Edited to flesh out a response. You're right that cursors are generally to be avoided, but for Dynamic SQL like you're doing they are indispensable. – Marc L. Sep 10 '15 at 03:47