Separate DDL and DML in Transact-SQL with Batches
I wrote about this problem in a Stackoverflow.com post called Problem with alter then update in try catch with tran using Transact-SQL where Mikael Eriksson gave me just the hint I needed. My problem came about when I tried to run the below Transact-SQL via sqlcmd (SQL Server 2005) (using the command: sqlcmd -S "localhost\SQLEXPRESS" -U sa -P password -i "C:\path\to\transform.sql"
).
USE PUK; BEGIN TRANSACTION; BEGIN TRY -- Modify the table RETRIEVAL_STAT alter table dbo.RETRIEVAL_STAT add SOURCE nvarchar(10) NULL, ACCOUNTNUMBER nvarchar(50) NULL, PUK nvarchar(20) NULL; -- Modify data in new columns of table RETRIEVAL_STAT update dbo.RETRIEVAL_STAT set SOURCE = 'XX', ACCOUNTNUMBER = 'XX', PUK = 'XX'; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber ,ERROR_SEVERITY() AS ErrorSeverity ,ERROR_STATE() AS ErrorState ,ERROR_PROCEDURE() AS ErrorProcedure ,ERROR_LINE() AS ErrorLine ,ERROR_MESSAGE() AS ErrorMessage; IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; IF @@TRANCOUNT > 0 COMMIT TRANSACTION; GO
Note that it contains code that alters a table (DDL
) followed by code that modifies data in a table (DML
). Also note that the DML is meant to work on the new columns introduced by the DDL. This code generates the below error.
(0 rows affected) Changed database context to 'PUK'. Msg 207, Level 16, State 1, Server localhost\SQLEXPRESS, Line 13 Invalid column name 'SOURCE'. Msg 207, Level 16, State 1, Server localhost\SQLEXPRESS, Line 13 Invalid column name 'ACCOUNTNUMBER'. Msg 207, Level 16, State 1, Server localhost\SQLEXPRESS, Line 13 Invalid column name 'PUK'.
The problem is that Transact-SQL is compiled first, then executed. It means that when the compiler goes through my SQL, it finds that the second block - the SQL that modifies the new columns - cannot possibly work because those columns don't exist yet. (Because it hasn't executed the first block yet, it is still compiling the whole lot!) The solution is simple enough. Place the blocks in separate batches
, which are compiled (and thus executed) separately, but still in order because I will still place the batches in the same file, one after the other, and run them with the same command. A batch
is a series of Transact-SQL statements (that may include a try-catch) separated by the GO
command.
This code works.
USE PUK; BEGIN TRANSACTION; BEGIN TRY -- Modify the table RETRIEVAL_STAT alter table dbo.RETRIEVAL_STAT add SOURCE nvarchar(10) NULL, ACCOUNTNUMBER nvarchar(50) NULL, PUK nvarchar(20) NULL; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber ,ERROR_SEVERITY() AS ErrorSeverity ,ERROR_STATE() AS ErrorState ,ERROR_PROCEDURE() AS ErrorProcedure ,ERROR_LINE() AS ErrorLine ,ERROR_MESSAGE() AS ErrorMessage; IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; IF @@TRANCOUNT > 0 COMMIT TRANSACTION; GO USE PUK; BEGIN TRANSACTION; BEGIN TRY -- Modify data in new columns of table RETRIEVAL_STAT update dbo.RETRIEVAL_STAT set SOURCE = 'XX', ACCOUNTNUMBER = 'XX', PUK = 'XX'; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber ,ERROR_SEVERITY() AS ErrorSeverity ,ERROR_STATE() AS ErrorState ,ERROR_PROCEDURE() AS ErrorProcedure ,ERROR_LINE() AS ErrorLine ,ERROR_MESSAGE() AS ErrorMessage; IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; IF @@TRANCOUNT > 0 COMMIT TRANSACTION; GO
Pages that helped me with this post.
- What are the difference between DDL, DML and DCL commands? Data Definition Language (DDL) statements are used to define the database structure or schema (create, alter, drop etc). Data Manipulation Language (DML) statements are used for managing data within schema objects (select, insert, update etc).
- My Stackoverflow.com post called Problem with alter then update in try catch with tran using Transact-SQL where Mikael Eriksson gave me just the hint I needed.
- MSDN page on GO (Transact-SQL) - remember that a GO statement marks the end of a batch. Something that confused me mightily up 'till now is this: GO is not a Transact-SQL statement; it is a command recognized by the sqlcmd and osql utilities and SQL Server Management Studio Code editor. I was getting very frustrated because whenever I tried testing SQL that contained
GO
in some tool other than an MS SQL Server tool (such as Execute Query, DbVisualizer or Eclipse, the code failed). Now I know why:GO
isn't SQL. - MSDN page on TRY...CATCH (Transact-SQL). A
try-catch
block cannot span multiplebatches
. It means you cannot put a GO within a try-catch, followed by more code.