views:

248

answers:

1

I'm working on a DB upgrade script that needs to drop a column (that part's easy). Before I can drop the column, I want to take the value and move it into another column that already exists.

I'm trying to acheive idempotence with my script, which is where I seem to be failing. I have code like this...

IF EXISTS (SELECT * 
         FROM sys.all_columns 
         WHERE sys.all_columns.object_id = OBJECT_ID(N'[dbo].[MyTable]')
         AND sys.all_columns.name = 'Column1')
BEGIN
    UPDATE [dbo].[MyTable] 
    SET [Column2] = [Column1]

    ALTER TABLE [dbo].[MyTable]
    DROP COLUMN [Column1]
END

No matter what I do, the UPDATE query is always executed, even if the condition is false, which causes errors. What do I need to do to prevent this query from being run if my initial condition is false? The ALTER TABLE statement below it only gets run if the condition is true.

+6  A: 

I'm guessing that when you say "the UPDATE query is always executed, which causes errors", you mean that you're getting the error:

Msg 207, Level 16, State 1 Line 6
Invalid column name 'Column1'.

This is not because the query is executing - this is a parsing error. The entire IF statement gets parsed by SQL Server before it is executed, and it is failing on the UPDATE statement because Column1 no longer exists. It's also failing on the ALTER TABLE statement, but SQL Server only shows you the first error.

Incidentally, this is why Shannon's example works - since both of her IF blocks are parsed at once, the second one that resembles yours parses just fine, because at the time the script is parsed, the column still exists. I suspect that if Shannon were to run her script again (minus the create table parts), the same error would appear.

To get around this, you just need to use dynamic SQL - it is not parsed until the line is actually run. You run dynamic SQL using the EXEC command, and you pass it the string you want to execute, like so:

IF EXISTS (SELECT * 
         FROM sys.all_columns 
         WHERE sys.all_columns.object_id = OBJECT_ID(N'[dbo].[MyTable]')
         AND sys.all_columns.name = 'Column1')
BEGIN
    EXEC('UPDATE [dbo].[MyTable] SET [Column2] = [Column1]')

    EXEC('ALTER TABLE [dbo].[MyTable] DROP COLUMN [Column1]')
END
Steve Broberg
+1 I should have put a batch seperator between first and second run of the conditional update.
Shannon Severance
Your solution worked, however the `ALTER TABLE` query didn't need to be run in an `EXEC`. It ran fine without it in an `EXEC`.
Dan Herbert
Hmm. SQL Server must treat the compilation of DDL statements different from DML statements when parsing query batches. Something to keep in mind.
Steve Broberg