使用生成的 DDL 时"<xyz> must be the first statement in a query batch"



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

我创建此脚本以循环遍历所有数据库并获取所有视图并通过执行 alter view 来刷新它们。 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

"GO"实际上不是有效的T-SQL。它只是一个字符串,各种 SQL 工具(如 SSMS)将其识别为批处理分隔符(就像单独运行每个块一样)。

因此,您可能也遇到了"'GO'附近的语法不正确"的错误。

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

EXEC OtherDatabase.dbo.sp_executesql @SQL;

感谢Bob Pusateri的博客提供了这种见解。

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

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

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 )

我喜欢将 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 "ProductionServerInstance" -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)."
    }
}

相关内容

最新更新