2010-03-17 9 views
6

Hej, chłopaki, więc o to chodzi.Jak wykonać T-SQL dla kilku baz danych, których nazwy są przechowywane w tabeli

Mam kilka baz danych (SqlServer 2005) na tym samym serwerze o tym samym schemacie, ale różnych danych.

Mam jedną dodatkową bazę danych, która ma jedną tabelę przechowującą nazwy wspomnianych baz danych.

Co muszę zrobić, to powtórzyć nazwę tych baz danych i właściwie "przełączać" się do każdego z nich (użyj [nazwa_bazy]] i wykonać skrypt T-SQL. Jestem czysty?

Podam przykład (uproszczony ze prawdziwy):

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

Załóżmy, że DatabaseA, DatabaseB i DatabaseC są prawdziwe istniejących baz danych. Załóżmy, że muszę utworzyć nowy SP na tych bazach danych. Potrzebuję skryptu, który zapętliłby się nad tymi bazami danych i wykonałby określony przeze mnie skrypt T-SQL (może być przechowywany w zmiennej varchar lub gdziekolwiek).

Wszelkie pomysły?

Dzięki!

Odpowiedz

2

Chyba to generalnie nie będzie możliwe w TSQL, ponieważ, jak inni wskazał,

  • trzeba najpierw jako oświadczenie użyć, aby zmienić bazę danych,

  • następnie instrukcja, którą chcesz wykonać, która jest, choć nie jest określona, ​​instrukcją DDL, która musi być pierwsza w grupie.

  • Co więcej, nie można mieć GO w ciągu znaków do EXECUTION.

znalazłem wiersza polecenia rozwiązaniem powołując 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()" 

Przykładowy kod wykorzystuje aktualną logowanie Windows do kwerendy wszystkich aktywnych baz danych z Master (zastąpić własnymi połączenia i zapytania do baz danych), a wykonuje dosłowne polecenie TSQL dla każdej odnalezionej bazy danych. (linie łamią tylko dla jasności)

Spójrz na command-line parameters of sqlcmd. Możesz również przekazać plik TSQL.

Jeśli chcesz umożliwić ręczny wybór baz danych, zajrzyj na stronę SSMS Tools Pack.

+0

Dzięki! Nie wiedziałem o pakiecie narzędzi SSMS. Spróbuję, bo to najprostszy sposób na zrobienie tego bez większego wysiłku. – emzero

0

użyć polecenia USE i powtórzyć swoje polecenia

Ps. Przyjrzeć się, jak korzystać z użycia z parametrem here

+1

W jaki sposób można użyć polecenia USE z literalnym ciągiem od wyniku ustawić? Mam na myśli, że mam nazwy DB w kolumnie varchar. – emzero

+0

@emzero, zobacz moją edycję powyżej, ale upewnij się, że sprawdzisz swoje dane wejściowe! – CResults

3

Najprostszym sposobem jest taka:

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 

Jednak oczywiście nie będzie działać do sprawozdania, które muszą być pierwszą instrukcją w partii, jak UTWÓRZ PROCEDURĘ. Do tego możesz użyć SQLCLR. Tworzenie i wdrażanie klasę tak:

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(); 
      } 
     } 
    } 
} 

Następnie można zrobić

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 
+0

Dzięki, ale to nie działa. Nie przełącza się to na bazy danych, wykonuje to, co piszę na "- Zrób, co chcesz" kilka razy na tym samym DB (ten, który ma tabelę DatabaseNames). – emzero

+0

To nie zadziała. PER MSDN w instrukcji EXEC "Zmiany w kontekście bazy danych trwają tylko do końca instrukcji EXECUTE." – JohnFx

+0

Kursory są zwykle złe, ale czasami konieczne (jak w tym przykładzie) +1 za to. @emzero + JohnFx - Powodem, dla którego to nie działa, jest to, że będziesz musiał zająć się swoim stwierdzeniem zmienną @stmt i exec() je jako wsad. – StingyJack

1

Powinieneś być w stanie to zrobić z sp_MSforeachdb nieudokumentowanych procedury przechowywanej.

+0

To nie daje kontroli nad tym, które bloki są wykonywane przeciwko. – Joe

+0

Pewnie, że tak! Musisz tylko użyć funkcji wbudowanej DB_NAME() w jakiejkolwiek kwerendzie, której używasz. Za jego pomocą można wykluczyć niektóre bazy danych lub je uwzględnić, w zależności od tego, która z nich jest bardziej odpowiednia dla aplikacji. – mwigdahl

1

Ta metoda wymaga, aby skrypt SQL był wykonywany na każdym zbiorze danych w zmiennej, ale powinien działać.

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 

Ponadto, jak niektórzy zwracają uwagę. Takie podejście jest problematyczne, jeśli chcesz uruchomić polecenie, które musi być jedyną rzeczą w pakiecie.

Oto alternatywa dla tej sytuacji, ale wymaga ona więcej uprawnień, niż wiele DBA może chcieć, i wymaga, aby umieścić swój SQL w osobnym pliku tekstowym.

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 
+0

Nie działa. 1: Nie ma takiego stwierdzenia, jak CREATE STORED PROCEDURE 2: CREATE PROCEDURE musi być pierwszym stwierdzeniem w partii. – erikkallen

+0

Naprawiono odpowiedź, aby uwzględnić metodę, która będzie działać z rzeczami, które muszą być we własnej partii. – JohnFx

0

Wiem, że to pytanie ma 5 lat, ale znalazłem to przez Google, więc inni mogą również.

Polecam procedurę przechowywaną systemu sp_msforeachdb. Nie musisz tworzyć żadnych innych procedur przechowywanych lub kursorów.

Biorąc tabela z nazwami baz danych jest już utworzony:

EXECUTE sp_msforeachdb ' 
USE ? 

IF DB_NAME() 
    IN(SELECT name DatabaseNames) 
BEGIN 

    SELECT 
       ''?'' as 'Database Name' 
      , COUNT(*) 
    FROM 
      MyTableName 
    ; 

END 
' 

zrobić to podsumować liczy w wielu bazach mam przywrócony z kilku różnych miejscach o tej samej zainstalowanej schematu bazy danych.

Przykład: - Powtórz wykonywanie poleceń SQL we wszystkich zarchiwizowanych bazach danych witryn.

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 
' 

Najtrudniejsza część to cudzysłowu '

Powiązane problemy