问题
I am developping a script that allows me to create all the views present in database1
in another database (database2
).
I am trying to do that using a cursor that loop on all the views of database1
and then try to execute the definition of that view on the second database.
Unfortunately it doesn't work. I get the following error:
Syntaxe incorrecte vers 'go'.
Msg 111, Niveau 15, État 1, Ligne 14
'CREATE VIEW' doit être la première instruction d'un traitement de requêtes.
This is my code
declare @database2 varchar(50), @database1 varchar(50)
set @database2 = 'Local'
set @database1 = 'prod'
declare @Query nvarchar(max), @view_definition nvarchar(max), @count int
set @count = 0
declare curseur cursor for SELECT top 1 view_definition FROM prod.information_schema.VIEWS
open curseur
fetch curseur into @view_definition
While @@FETCH_STATUS = 0
Begin
set @count = @count + 1
--Print 'Vue N° ' + cast(@count as varchar) + ':'
set @Query = N'Use ' + @database2 +CHAR(13)+CHAR(10)+ 'go' + @view_definition +CHAR(13)+CHAR(10)+ 'go'
print @Query
exec sp_executesql @Query
fetch curseur into @view_definition
End
Close curseur
deallocate curseur
This code was executed from database1.
However when I execute the result of the 'print @Query' instruction, it works!!
Does anyone can help me to resolve the problem?
Thanks in advance
回答1:
There are two things here:
- You can't USE another database in a stored procedure, although is the other database is on the same server you can use
CREATE VIEW databasename.schemaname.viewname (...
. If it's on another server, yiu could try setting it up as a linked server and using servername.database.schema.viewname. - sp_executesql expects one statement, and doesn't accept GO to my knowledge (which is what the error is telling you). You could try to put in ';' instead (and you don't need the CHAR(13)'s if you do although they make it easier to read).
回答2:
In case this helps someone in the future, here's my solution to this problem. The full history of how I came up with it is over at Stored Procedure to Copy Views from Current Database to Another
CREATE PROCEDURE [dbo].[usp_Copy_View_To_Database]
@ViewName SYSNAME, -- The name of the view to copy over
@DatabaseName SYSNAME, -- The name of the database to copy the view to
@overwrite bit = 1 -- Whether to overwrite any existing view
AS
IF DB_ID(@DatabaseName) IS NULL -- Validate the database name exists
BEGIN
RAISERROR('Invalid Destination Database Name passed',16,1)
RETURN
END
SET NOCOUNT ON
IF @overwrite = 1 -- If set to overwrite, try to drop the remote view
BEGIN
DECLARE @DropViewStatement NVARCHAR(MAX) =
'EXEC ' + QUOTENAME(@DatabaseName) + '.sys.sp_executesql N''DROP VIEW IF EXISTS ' + QUOTENAME(@ViewName) + ';'';'
EXEC (@DropViewStatement);
END
-- Extract the saved definition of the view
DECLARE @ViewDefinition NVARCHAR(MAX);
SELECT @ViewDefinition = definition FROM sys.sql_modules WHERE [object_id] = OBJECT_ID(@ViewName);
-- Check for a mismatch between the internal view name and the expected name (TODO: Resolve this automatically?)
IF @ViewDefinition NOT LIKE ('%' + @ViewName + '%')
BEGIN
DECLARE @InternalName NVARCHAR(MAX) = SUBSTRING(@ViewDefinition, 3, CHARINDEX(char(10), @ViewDefinition, 3)-4);
PRINT ('Warning: The view named '+@ViewName+' has an internal definition name that is different ('+@InternalName+'). This may have been caused by renaming the view after it was created. You will have to drop and recreate it with the correct name.')
END
-- Substitute any hard-coded references to the current database with the destination database
SET @ViewDefinition = REPLACE(@ViewDefinition, db_name(), @DatabaseName);
-- Generate the dynamic SQL that will create the view on the remote database
DECLARE @CreateViewStatement NVARCHAR(MAX) =
'EXEC ' + QUOTENAME(@DatabaseName) + '.sys.sp_executesql N''' + REPLACE(@ViewDefinition,'''','''''') + ''';'
--PRINT '@CreateViewStatement: ' + @CreateViewStatement -- Can be used for debugging
-- Execute the create statement
EXEC (@CreateViewStatement);
In short, you need two layers of nested dynamic SQL:
- Inner layer to execute the "CREATE VIEW" statement, which must be on its own. This is executed using
EXEC SomeDatabase.sys.sp_executesql @CreateViewSQL
- One more layer to dynamically specify "SomeDatabase" via a parameter (assuming you require the flexibility of copying it to a database not known at scripting time).
Calling the above stored proc in the inner-loop of the original poster's tentative solution should solve the problem of copying a view to another database.
Note that simply looping over all views might pose a challenge if views depend on one another. There may be some additional complexity involving resolving the dependency tree of views an copying them in the correct order. Alternatively, a "dumb and easy" approach might be to loop over all views, ignore failures, and keep repeating the process until all views have been created.
来源:https://stackoverflow.com/questions/35336718/copy-a-view-definition-from-one-database-to-another-one-in-sql-server