【问题标题】:"<xyz> must be the first statement in a query batch" when using generated DDL使用生成的 DDL 时“<xyz> 必须是查询批处理中的第一条语句”
【发布时间】:2014-03-01 06:00:38
【问题描述】:

我的服务器有 100 多个链接到其他数据库服务器的数据库。这些数据库具有链接表的视图。我需要每周更新链接服务器中任何对象更改的视图,即添加列。

我创建了这个脚本来遍历所有数据库并获取所有视图并通过更改视图刷新它们。 sp_refreshview 不适用于链接服务器。

当我打印@sql 变量时,它在另一个查询窗口中运行良好。当我尝试执行 @sql 变量时,它给了我以下错误:

Msg 111, Level 15, State 1, Line 3
'ALTER VIEW' must be the first statement in a query batch.

我认为这与 LF/CR 有关。我尝试了很多方法都没有运气。

有什么想法吗?

DECLARE @command varchar(1000) 
CREATE TABLE #tempViewSQL (DBName VARCHAR(255)
                          ,ViewSQL VARCHAR(4000))

SELECT @command = 'IF ''?'' NOT IN(''master''
                                 , ''model''
                                 , ''msdb''
                                 , ''tempdb''
                                 ,''pubs''
                                 ,''AuditProduction''
                                 ,''AuditProductionTest''
                                 ,''IID_Support''
                                 ,''Insurance_Files''
                                 ,''LoansAnalysis''
                                 ,''QualityAudit''
                                 ,''QualityAuditTest'') 
                BEGIN 
                  USE ? 
               INSERT INTO #tempViewSQL 
               SELECT TABLE_CATALOG, replace(view_definition,''create view'',''alter view'') 
                 FROM information_schema.views 
                WHERE TABLE_NAME NOT IN (''syssegments'',''sysconstraints'')
                 END' 

EXEC sp_MSforeachdb @command


DECLARE @SQLCursor VARCHAR(2000)
DECLARE @SQL VARCHAR(2000)
DECLARE @DbName VARCHAR(255)
DECLARE MyCursor CURSOR FOR
SELECT DBName, ViewSQL FROM  #tempViewSQL

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @DbName,@SQLCursor

WHILE @@FETCH_STATUS = 0   
BEGIN   


SET @SQL = 'USE ' + @DBName + CHAR(10) + CHAR(13) + 'GO' + CHAR(10) + CHAR(13) + @SQLCursor 
--PRINT (@SQL)
EXECUTE (@SQL)

FETCH NEXT FROM MyCursor INTO @DbName,@SQLCursor

END

CLOSE MyCursor   
DEALLOCATE MyCursor 

DROP TABLE #tempViewSQL

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    "GO" is not actually valid T-SQL. 它只是一个字符串,各种 SQL 工具(如 SSMS)将其识别为批处理分隔符(就像您单独运行每个块一样)。

    所以你可能也遇到了“'GO'附近的语法不正确”这样的错误。

    为了在另一个数据库中创建视图,您需要在该数据库的上下文中运行 sp_executesql,例如:

    EXEC OtherDatabase.dbo.sp_executesql @SQL;
    

    感谢Bob Pusateri's blog 的见解。

    但是,您有一个动态数据库名称,这使得它更加复杂。我相信您可能可以执行包含 sp_executesql 命令的动态 SQL,该命令使用动态数据库名称(Incpetion-style)限定。但是你必须小心你的单引号编码。

    【讨论】:

      【解决方案2】:

      您可以尝试使用 sp_executesql SP 在特定数据库中执行查询,如下所示::

      DECLARE @AlterQuery NVARCHAR(MAX) = N'ALTER VIEW v1 AS SELECT * from T1' 
      DECLARE @DbName NVARCHAR(MAX) = 'Test'
      DELARE @Query NVARCHAR(MAX) = 'exec [' + @DbName + '].sys.sp_executesql N''' + REPLACE( @AlterQuery, '''', '''''' ) + ''''
      EXECUTE( @Query )
      

      【讨论】:

        【解决方案3】:

        我喜欢在这样的部署中使用 Powershell一些逻辑。它不是纯 SQL 解决方案,但可以轻松修改以完成工作...

        function Get-ProductionDatabases
        {
            param
            (
                [Parameter(Mandatory=$true)]
                [string]$centralServer,
                [Parameter(Mandatory=$true)]
                [string]$centralDatabase
            )
        
            $conn = New-Object System.Data.SqlClient.SqlConnection "Server=$centralServer;Database=$centralDatabase;Integrated Security=SSPI;"; 
            $dt   = New-Object System.Data.DataTable;    
        
            $cmd = $conn.CreateCommand(); 
            $cmd.CommandType = [System.Data.CommandType]::Text
            $cmd.CommandText = "Select  [ServerName], 
                                        [DatabaseName]
                                From    [dbo].[ProductionDatabases];";
        
            $conn.Open();
            $dt.Load($cmd.ExecuteReader());
            $conn.Close();    
        
            $dt
        }
        
        
        $productionDatabases = Get-ProductionDatabases -centralServer "ProductionServer\Instance" -centralDatabase "CentralDatabase"
        
        foreach($db in $productionDatabases)
        {
            $conn = New-Object System.Data.SqlClient.SqlConnection "Server=$($db.ServerName);Database=$($db.DatabaseName);Integrated Security=SSPI;"; 
            $queryOut = New-Object System.Data.DataTable;    
        
            $cmd = $conn.CreateCommand(); 
            $cmd.CommandType = [System.Data.CommandType]::Text
            $cmd.CommandText = "Exec sp_refreshview;";                        
        
            $conn.Open();
        
            try
            {
                $queryOut.Load($cmd.ExecuteReader());
                $conn.Close();           
            }
            catch
            {
                "Warning: Error connecting to $($db.ServerName)."
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多