【问题标题】:Executing SQL query on multiple databases在多个数据库上执行 SQL 查询
【发布时间】:2016-09-05 18:47:10
【问题描述】:

我知道我的帖子与本论坛其他帖子的标题非常相似,但我真的找不到我需要的答案。

这是我的问题,我的 Windows Server 上运行着一个 SQL Server。在我的 SQL Server 中,我有大约 30 个数据库。它们都具有相同的表和相同的存储过程。

现在,问题来了,我有一个庞大的脚本,需要在所有这些数据库中运行。我希望我可以对我的所有数据库只执行一次。

我尝试了一些方法,例如转到“查看”>>注册服务器>>本地服务器组>>新服务器注册。但是这个解决方案适用于很多服务器,而不是很多数据库。

我知道我可以通过输入数据库名称来完成,但是查询真的很大,所以在所有数据库中运行需要很长时间。

有人知道这是否可能吗?

【问题讨论】:

  • 您无法缩短针对不同数据库执行查询所需的时间。这似乎是一个 duplicate of question posted on DBA 应该可以帮助你:-)
  • 为什么输入数据库名称需要很长时间才能在所有数据库中运行?
  • 您可以使用 EXEC sp_MSforeachdb @stored_procedure。 ,

标签: sql sql-server database


【解决方案1】:

ApexSQL Propagate 是可以在这种情况下提供帮助的工具。它用于在多个数据库甚至多个服务器上执行单个或多个脚本。您应该做的只是选择该脚本,然后选择您要执行该脚本的所有数据库:

当您加载脚本和数据库时,您只需单击“执行”按钮并等待结果:

【讨论】:

  • 它有其怪癖(编码、我的旧 SQL 2008 实例有问题、在执行时覆盖 .sql 源文件),但总体上看起来还不错。此外,第一个设置有点笨拙。它甚至在覆盖其中一个输入 .sql 文件时设法清空它。再一次,看起来还可以。
  • 它不起作用,我点击“执行”,它似乎运行得很好,直到它完成然后它对所有数据库显示“失败”。我测试了我的连接,它们很好所以我不知道为什么它不会运行我的脚本。
【解决方案2】:

您可以使用WHILE 循环所有数据库名称,并在循环内使用EXECUTE 执行查询。我认为 SET @dbname = ... 的声明可能会更好,但这也有效。

DECLARE @rn INT = 1, @dbname varchar(MAX) = '';
WHILE @dbname IS NOT NULL 
BEGIN
    SET @dbname = (SELECT name FROM (SELECT name, ROW_NUMBER() OVER (ORDER BY name) rn 
        FROM sys.databases WHERE name NOT IN('master','tempdb')) t WHERE rn = @rn);

    IF @dbname <> '' AND @dbname IS NOT NULL
        EXECUTE ('use ['+@dbname+'];

            /* Your script code here */
            UPDATE some_table SET ... ;

        ');
    SET @rn = @rn + 1;
END;

【讨论】:

    【解决方案3】:

    考虑从 SSMS(查询--SQLCMD 模式)运行 SQLCMD Mode 中的脚本。这样,您可以将脚本保存到文件中,并轻松地在每个所需数据库的上下文中运行它:

    USE DB1;
    :r C:\SqlScript\YourLargeScript.sql
    GO
    USE DB2;
    :r C:\SqlScript\YourLargeScript.sql
    GO
    USE DB3;
    :r C:\SqlScript\YourLargeScript.sql
    GO
    

    此技术还可用于通过添加:CONNECT 命令对其他服务器上的数据库运行脚本。执行整个脚本后,连接恢复到初始服务器/数据库:

    :CONNECT SomeServer
    USE DB4;
    :r C:\SqlScript\YourLargeScript.sql
    GO
    :CONNECT SomeOtherServer
    USE DB5;
    :r C:\SqlScript\YourLargeScript.sql
    GO
    

    重要提示:注意GO 需要使用批处理分隔符,:CONNECT 才能按预期工作。我建议在调用脚本中包含GO,就像上面的示例一样,但GO 作为:r 脚本文件中的最后一行也将提供所需的结果。在此示例中(或在脚本文件末尾)没有GO,脚本将在 SomeServer 上运行两次,并且根本不会针对 SomeOtherServer 运行。

    【讨论】:

    • Dan Guzman,我认为你的建议是最好的方法,因为我的文件有大约 45000 行代码。我要试试,让大家知道!非常感谢
    【解决方案4】:

    你可以这样写脚本

    DECLARE CURSOR_ALLDB_NAMES CURSOR FOR
    SELECT name 
    FROM Sys.Databases
    WHERE name NOT IN('master', 'tempdb') 
    
    OPEN CURSOR_ALLDB_NAMES
    
    FETCH CURSOR_ALLDB_NAMES INTO @DB_NAME
    
    WHILE @@Fetch_Status = 0
    BEGIN
      EXEC('UPDATE '+ @DB_NAME + '..SameTableNameAllDb SET Status=1')
      FETCH CURSOR_ALLDB_NAMESINTO INTO @DB_NAME
    END
    
    CLOSE CURSOR_ALLDB_NAMES
    

    【讨论】:

    • 不起作用。一个问题是最后一个 FETCH 语句中的游标名称,但即使修复它,我也会得到非常奇怪的行为。
    • @OskarBerggren 我想通了。在FETCH CURSOR_ALLDB_NAMESINTO INTO @DB_NAME 之后你需要声明FETCH NEXT FROM CURSOR_ALLDB_NAMES INTO @DB_NAME 否则你会在第一条记录上得到一个无限循环
    【解决方案5】:

    这是正常的做法:

    假设您想在数据库 DBO 上进行选择,而不是:

    select * from DBOther..TableName
    

    还要检查表或视图是否在 dbo 架构上,如果不是,您还应该添加架构:请注意我现在在数据库名称后只使用一个点

    select * from DBOther.dbo.ViewName
    

    如果任何数据库位于另一台机器上的另一台服务器上,请确保该数据库位于链接服务器中。
    然后您可以通过以下方式访问该数据库上的表或视图:

    SELECT * FROM [AnotherServerName].[DB].[dbo].[Table]
    

    这是另一种不需要输入数据库名称的方法:

    use DB1
    go
    
    select * from table1
    go
    
    use DB2
    go
    
    select * from table1
    go
    

    请注意,这只有在每个数据库上的表和字段完全相同时才有效

    【讨论】:

      【解决方案6】:

      您可以使用以下脚本在一组数据库上运行相同的脚本。只需更改 insert 行中的过滤器即可。

      
      declare @dbs table (
        dbName varchar(100),
        done bit default 0
      )
      
      insert @dbs select [name], 0 FROM master.dbo.sysdatabases WHERE [Name] like 'targets_%'
      
      while (exists(select 1 from @dbs where done = 0))
      begin
        declare @db varchar(100);
        select top 1 @db = dbName from @dbs where done = 0;
        exec ('
          use [' + @db + '];
          update table1 set
            col1 = '''',
            col2 = 1
          where id = ''45b6facb-510d-422f-a48c-687449f08821''
        ');
        print @db + ' updated!';
        update @dbs set done = 1 where dbName = @db;
      end
      
      

      如果您的 SQL Server 版本不支持表变量,只需使用临时表,但不要忘记在脚本末尾删除它们。

      【讨论】:

        【解决方案7】:

        根据要求,您可以这样做:

        
        declare @dbName nvarchar(100)
        declare @script nvarchar(max)
        declare @dbIndex bigint = 0
        declare @dbCount bigint = (
            select count(*) from
                sys.databases
        
            )
        declare crs_databases cursor for
        (
            select
                [name]
            from
                sys.databases   
        )
        open crs_databases
        fetch next from crs_databases into @dbName
        while @@FETCH_STATUS = 0
        begin
            set @dbIndex = @dbIndex+1
            set @script = concat(@script,
                ' select Id from ['+@dbName+']..YourTableName ',        
                case 
                    when @dbIndex = @dbCount then ''
                    else 'union'
                end)
            fetch next from crs_databases into @dbName
        end
        
        select @script
        close crs_databases
        deallocate crs_databases
        
        

        请注意,双点表示法假定架构是 dbo。否则,您需要明确写下架构。

        select Id from ['+@dbName+'].schema.YourTableName
        

        当需要在每台服务器上执行存储过程时,@script变量会有另外的内容。

        【讨论】:

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