问题
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