sp_MSForEachDB似乎不喜欢GO

时间:2013-11-21 15:45:06

标签: sql sql-server

我之前使用过此SP。现在,我正在尝试使用下面的代码允许用户使用相同字母开头的50个奇数数据库。看起来它在代码中不喜欢“GO”。这是为什么 ?什么是工作?

感谢您的时间.. :)

RM

exec sp_MSForEachDB
'
IF ''?'' LIKE ''MYDBNames%''
BEGIN
Use [?]
Go
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts]
GO
EXEC sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts''
GO
END

5 个答案:

答案 0 :(得分:4)

我昨天在另一个问题中解释了这一点(here)。这个本质是这样的:GO不是SQL语句,它是一个SSMS / SQLCMD命令,用于分离批处理(一起编译的SQL语句组)。所以你不能在存储过程或动态SQL之类的东西中使用它。此外,很少有语句上下文可以跨越GO边界(事务和会话级临时表是关于它的。)

但是,因为存储过程和动态SQL都建立了自己独立的批处理/执行上下文,所以您可以使用它们来解决GO的正常需求,如下所示:

exec sp_MSForEachDB
'
    IF ''?'' LIKE ''MYDBNames%''
    BEGIN
    Use [?]
    EXEC(''
        CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts]
        '')
    EXEC('' EXEC sp_addrolemember N''''db_owner'''', N''''MYDOMAIN\Analysts'''' '')
    END
'

答案 1 :(得分:2)

单词GO是批处理分隔符,不是SQL关键字。在SSMS中,您可以转到选项并将其更改为任何内容 - 例如COME

试试这个:

exec sp_MSForEachDB
'
IF ''?'' LIKE ''MYDBNames%''
BEGIN;
Use [?];
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts];
EXEC sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts'';
END;'

答案 2 :(得分:1)

  1. 只是停止使用GO,这不是必需的(它甚至不是T-SQL;它只是Management Studio的批处理分隔符)。
  2. 停止使用sp_MSForEachDB。哦,问题(如果你想要证明,请参阅hereherehere)。
  3. 以下是不使用任何存储过程执行此操作的代码:

    DECLARE @sql NVARCHAR(MAX) = N'';
    
    SELECT @sql += N'USE ' + QUOTENAME(name) + ';
      CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts];
      EXEC sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts'';
    
    '
    FROM sys.databases
    WHERE state = 0 -- online
    AND name LIKE N'MyDBName%';
    
    PRINT @sql;
    -- EXEC sp_executesql @sql;
    

答案 3 :(得分:0)

为什么不使用光标?

他们非常适合这样的管理任务。只要记录集很小,它们就能表现良好。

下面的代码为每个使用;的数据库创建一个SQL语句。结合多个任务。

与上述代码不同,它会在创建新用户之前删除所有现有用户。避免可能的问题。

然后它执行代码。如果需要,您甚至可以将这些adhoc请求记录到内部表中并添加错误检查。

祝你好运。

--
--  EXEC same statements against several like databases
--

    -- Declare local variables
    DECLARE @STMT NVARCHAR(4000);

    -- Allocate cursor, return table names
    DECLARE MYTRG CURSOR FAST_FORWARD FOR 
      SELECT 
          ' use [' + ltrim(rtrim(d.name)) + ']; ' +
          ' IF  EXISTS (SELECT * FROM sys.database_principals WHERE name = N''[MYDOMAIN\Analysts]'') DROP USER [MYDOMAIN\Analysts]; ' +
          ' CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts] WITH DEFAULT_SCHEMA=[DBO]; ' +
          ' sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts''; ' as STMT 
      FROM master.dbo.sysdatabases d
      WHERE d.name like 'MYDBNames%'
      ORDER BY d.name;

    -- Open cursor    
    OPEN MYTRG;

    -- Get the first row    
    FETCH NEXT FROM MYTRG INTO @STMT;

    -- While there is data
    WHILE (@@FETCH_STATUS = 0) 
    BEGIN   

        -- Show detail database info
    EXEC sp_executesql @STMT;

    -- Get the first row    
    FETCH NEXT FROM MYTRG INTO @STMT;

    END;

    -- Close the cursor
    CLOSE MYTRG; 

    -- Release the cursor
    DEALLOCATE MYTRG; 

答案 4 :(得分:0)

与@ RBarryYoung的答案一样,当命令带有必须是“批处理中的第一个”的要求时,可以使用嵌入式exec()来避免需要“go”。我不认为问题的例子需要“去”,但标题本身可能会引导人们到这里来。

这是一个基本的SP来包装这个支持的DDL语句,如“create proc”,并提供一些错误消息。我可能是第五个做这件事的人,这个人不是最终的全部。

Blessing / Curse:调用Common.ufn_UsvToNVarcharKeyTable(),您可能需要在自己的CSV分割器中进行修补或删除:

alter proc [Common].[usp_ForEachDatabase]   
    @pSql                 nvarchar(max),
    @pDatabaseNameLikeOpt nvarchar(300) = null,   -- Optional pattern to match with like
    @pDatabaseNameCsvOpt  nvarchar(max) = null,   -- Optional list of DBs
    @pJustPrintDbNames    bit = 0,                -- Don't exec, just print database names
    @pDebug               bit = 1                 -- may add additional print statements
as
/*-----------------------------------------------------------------------------------------------------------------
    Purpose:  Execute SQL on each database.  Replacement for the standard sp_MSForEachDB which has a size character 
              limit and requires an exec() within for DDL commands which must be the first in a batch.

     Sources: Ideas from http://www.experts-exchange.com/Database/MS-SQL-Server/SQL-Server-2005/Q_26337388.html
              http://stackoverflow.com/questions/20125430
              http://stackoverflow.com/questions/1819095/

    Modified    By          Description
    ----------  ----------  -----------------------------------------------------------------------------------
    2014.10.14  crokusek    Initial version. Pieces from internet.
  --------------------------------------------------------------------------------------------------------------*/
begin try

    declare databaseCursor cursor local forward_only static for
        select IsNull(sd.Name, ud.Name) as Name,
               --
               -- If a list was specified, flag when a name was not found
               --
               convert(bit, iif(sd.Name is null, 1, 0)) as IsNotFound   
          from 
             (
                select Name from sys.databases
                 where Name not in ('master', 'tempdb', 'model', 'msdb')                            
                   and is_distributor = 0 -- http://stackoverflow.com/questions/1819095/
             ) sd             
          full outer join ( select Value as Name from Common.ufn_UsvToNVarcharKeyTable(@pDatabaseNameCsvOpt, ',' ) ) ud
            on ud.Name = sd.Name            
         where (@pDatabaseNameLikeOpt is null or IsNull(sd.Name, ud.Name) like @pDatabaseNameLikeOpt)
           and (@pDatabaseNameCsvOpt is null or ud.Name is not null)
         order by IsNull(sd.Name, ud.Name);

    declare 
        @matchingDatabaseNames nvarchar(max),
        @databaseName nvarchar(300),
        @isNotFound bit,
        @errorCount int = 0,
        @successCount int = 0,
        --
        -- Use an embedded exec() to place the user command(s) in a separate context to avoid errors like: 
        --      CREATE/ALTER PROCEDURE must be the first statement in a query batch.
        --         
        @sqlExec nvarchar(max) = N'exec (''' + replace(@pSql, '''', '''''') + ''')';

    open databaseCursor
    fetch next from databaseCursor into @databaseName, @isNotFound;
    while @@fetch_status = 0
    begin                                                
        if (@isNotFound = 1)
        begin
            print 'Error: Database ' + @databaseName + ' was not found.';
            set @errorCount += 1;        
        end
        else
        begin
            set @matchingDatabaseNames = coalesce(@matchingDatabaseNames + ',', '') + @databaseName; -- Create Csv

            print 'Database: ' + @databaseName;    

            if (@pJustPrintDbNames = 0)
            begin        
                declare 
                    @execSql nvarchar(max) = 'use ' + @databaseName + ';' + char(10) + @sqlExec;

                begin try
                    exec (@execSql)  
                    set @successCount += 1;        
                end try
                begin catch
                    select @databaseName as [Database],
                           error_number() as ErrorNumber,
                           error_severity() as ErrorSeverity,
                           error_state() as ErrorState,
                           error_procedure() as ErrorProcedure,
                           error_line() as ErrorLine,
                           error_message() as ErrorMessage;
                    set @errorCount += 1;        
                end catch  
            end
        end
        fetch next from databaseCursor into @databaseName, @isNotFound;
    end

    if (@pJustPrintDbNames = 1)
        print @matchingDatabaseNames; -- this can then be used as input
    else                                                
        print 'Completed with ' + convert(varchar(30), @errorCount) + ' Errors ' + 
              'and ' +  convert(varchar(30), @successCount) + ' Successes.';
end try
begin catch
     if (xact_state() = -1)
        rollback;        
    -- Log / Rethrow
end catch
go

用法:

declare 
    @sql nvarchar(max) = N'
create proc usp_ReplaceEachSingleQuoteWithTwoSingleQuotesInTheDefinition
...';

exec [Common].[usp_ForEachDatabase]   
    @pSql                 = @sql,
    @pDatabaseNameLikeOpt = null, 
    @pDatabaseNameCsvOpt  = null,
    @pJustPrintDbNames    = 0,
    @pDebug               = 1;