代码之家  ›  专栏  ›  技术社区  ›  empz

如何对名称存储在表中的多个数据库执行T-SQL

  •  7
  • empz  · 技术社区  · 16 年前

    我在同一台服务器上有几个数据库(SQLServer2005),它们具有相同的模式,但数据不同。

    所以我需要做的是遍历这些数据库名称,并实际“切换”到每个数据库(使用[dbname]),然后执行一个T-SQL脚本。我明白了吗?

    让我给你举个例子(从真实的简化):

    CREATE TABLE DatabaseNames
    (
       Id   int,
       Name varchar(50)
    )
    INSERT INTO DatabaseNames SELECT 'DatabaseA'
    INSERT INTO DatabaseNames SELECT 'DatabaseB'
    INSERT INTO DatabaseNames SELECT 'DatabaseC'
    

    假设我需要在这些数据库上创建一个新的SP。我需要一些脚本在这些数据库上循环并执行我指定的T-SQL脚本(可能存储在varchar变量上或任何地方)。

    有什么想法吗?

    6 回复  |  直到 5 年前
        1
  •  2
  •   devio    16 年前

    我想这在TSQL中通常是不可能的,因为正如其他人所指出的,

    • 首先需要as USE语句来更改数据库,

    • 后面是要执行的语句,虽然没有指定,但它是一个DDL语句,必须是批处理中的第一个。

    • 此外,不能在字符串中执行GO。

    我找到了一个调用sqlcmd的命令行解决方案:

    for /f "usebackq" %i in 
        (`sqlcmd -h -1 -Q 
         "set nocount on select name from master..sysdatabases where status=16"`)
        do
            sqlcmd -d %i -Q "print db_name()"
    

    示例代码使用当前Windows登录从Master查询所有活动数据库(替换为您自己的数据库连接和查询),并对由此找到的每个数据库执行文本TSQL命令(换行符(仅为清晰起见)

    看一看这个 command-line parameters of sqlcmd . 您也可以向它传递一个TSQL文件。

    SSMS Tools Pack .

        2
  •  3
  •   erikkallen    16 年前

    最简单的方法是:

    DECLARE @stmt nvarchar(200)
    DECLARE c CURSOR LOCAL FORWARD_ONLY FOR SELECT 'USE [' + Name + ']' FROM DatabaseNames
    OPEN c
    WHILE 1 <> 0 BEGIN
        FETCH c INTO @stmt
        IF @@fetch_status <> 0 BREAK
        SET @stmt = @stmt + ' ' + @what_you_want_to_do
        EXEC(@stmt)
    END
    CLOSE c
    DEALLOCATE c
    

    但是,对于需要成为批处理中第一条语句的语句,它显然不起作用,比如CREATE过程。为此,可以使用SQLCLR。创建并部署如下类:

    public class StoredProcedures {
        [SqlProcedure(Name="exec_in_db")]
        public static void ExecInDb(string dbname, string sql) {
            using (SqlConnection conn = new SqlConnection("context connection=true")) {
                conn.Open();
                using (SqlCommand cmd = conn.CreateCommand()) {
                    cmd.CommandText = "USE [" + dbname + "]";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = sql;
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }
    

    DECLARE @db_name nvarchar(200)
    DECLARE c CURSOR LOCAL FORWARD_ONLY FOR SELECT Name FROM DatabaseNames
    OPEN c
    WHILE 1 <> 0 BEGIN
        FETCH c INTO @@db_name
        IF @@fetch_status <> 0 BREAK
        EXEC exec_in_db @db_name, @what_you_want_to_do
    END
    CLOSE c
    DEALLOCATE c
    
        3
  •  2
  •   mwigdahl    16 年前

    你应该可以用 sp_MSforeachdb 未记录的存储过程。

        4
  •  2
  •   JohnFx    16 年前

    此方法要求您将要在变量中的每个DB上执行的SQL脚本放在一个位置,但应该可以工作。

    DECLARE @SQLcmd varchar(MAX)
    SET @SQLcmd ='Your SQL Commands here'
    
    DECLARE @dbName nvarchar(200)
    DECLARE c CURSOR LOCAL FORWARD_ONLY FOR SELECT dbName FROM DatabaseNames
    OPEN c
    WHILE 1 <> 0 BEGIN
        FETCH c INTO @dbName 
        IF @@fetch_status <> 0 BREAK
        EXEC('USE [' + @dbName + '] ' + @SQLcmd )
    END
    CLOSE c
    

    对于这种情况,这里有一个替代方法,但是它需要比许多DBA可能希望您拥有的更多的权限,并且需要您将SQL放入一个单独的文本文件中。

    DECLARE c CURSOR LOCAL FORWARD_ONLY FOR SELECT dbName FROM DatabaseNames
    OPEN c
    WHILE 1 <> 0 BEGIN
        FETCH c INTO @dbName 
        IF @@fetch_status <> 0 BREAK
         exec master.dbo.xp_cmdshell 'osql -E -S '+ @@SERVERNAME + ' -d ' + @dbName + '  -i c:\test.sql'
    END
    CLOSE c
    DEALLOCATE c
    
        5
  •  0
  •   Community Mohan Dere    9 年前

    USE 命令并重复你的命令

    here

        6
  •  0
  •   Joe B    10 年前

    我知道这个问题有5年历史了,但我是通过谷歌找到的,所以其他人也可能会问。

    如果已创建数据库名称表:

    EXECUTE sp_msforeachdb '
    USE ? 
    
    IF DB_NAME() 
        IN( SELECT name DatabaseNames )
    BEGIN
    
        SELECT 
                  ''?'' as 'Database Name'  
                , COUNT(*)
        FROM
                MyTableName
        ;
    
    END
    '
    

    我这样做是为了总结我从多个不同站点使用相同的已安装数据库架构还原的许多数据库中的计数。

    例子:

    PRINT       'Database Name'
    + ',' +     'Site Name'
    + ',' +     'Site Code'
    + ',' +     '# Users'
    + ',' +     '# Seats'
    + ',' +     '# Rooms'
    ...  and so on...
    + ',' +     '# of days worked'
    ;
    
    
    EXECUTE sp_msforeachdb 'USE ?
    
    IF DB_NAME() 
        IN( SELECT name FROM sys.databases  WHERE name LIKE ''Site_Archive_%'' )
    BEGIN
    
    DECLARE  @SiteName      As Varchar(100);
    DECLARE  @SiteCode      As Varchar(8);
    
    DECLARE  @NumUsers  As Int
    DECLARE  @NumSeats  As Int
    DECLARE  @NumRooms  As Int
    ... and so on ...
    
    SELECT  @SiteName = OfficeBuildingName FROM  Office
    ...
    
    SELECT @NumUsers = COUNT(*)  FROM   NetworkUsers
    ...
    
    PRINT       ''?'' 
    + '','' +     @SiteName 
    + '','' +     @SiteCode
    + '','' +     str(@NumUsers)
    ...
    + '','' +     str(@NumDaysWorked) ;
    END
    '
    

    推荐文章