Problem:
- you want / need to use dynamic SQL to create procedure / function / view / trigger in other databases than the current
- you can't specify the database name in the statement,
CREATE VIEW tempdb.dbo.v_test AS SELECT 1
; will fail with Error 166: 'CREATE/ALTER VIEW' does not allow specifying the database name as a prefix to the object name. - the
CREATE
(orALTER
) statement needs to be the first in the batch, otherwise you'll get the error 111: 'CREATE ...' must be the first statement in a query batch.. For This reason you can't simply writeUSE <database>
before theCREATE
:
DECLARE @create_sql NVARCHAR(max);
SET @create_sql = CONCAT('USE tempdb', CHAR(13) + CHAR(10),
'CREATE VIEW dbo.v_test AS SELECT 1 AS n;'
);
EXEC (@create_sql) -- fails
-- or
EXEC sys.sp_executesql @create_sql -- fails too
- you can't use
GO
inside anEXEC()
orEXEC sys.sp_executesql
statement, because it is not in the SQL standard (just a configurable dummy word used inside SSMS and several other SQL tools), so you can't simply add it between the USE and the CREATE command in the script above as you would do it in SSMS when creating a "normal" deployment script - the internal procedure
sys.sp_executesql
exists in every database and executes the given command in its own database, but there may be many (e.g. hundred) databases, so that you can't really use multipleIF
statements to "hardcode" where you want to execute it
EXEC SomeOtherDatabase.sys.sp_executesql @create_sql;
EXEC SomeOtherDatabase
, because it could be any (even a database, that your coworker created 5 minutes ago)