sp_MSForEachDB doesn't seem to like GO - sql

I have used this SP before. Now, I am trying to permission a user to 50 odd databases that start with the same letters, using the code below. It looks like it does not like "GO" in the code. Why is that ? and what is the work around?
Thanks for your time.. :)
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
'

I just explained this in another question yesterday (here). This essence is this: GO isn't a SQL statement, it's an SSMS/SQLCMD command that is used to separate batches (groups of SQL statements that are compiled together). So you cannot use it in things like stored procedures or Dynamic SQL. Also, very few statement contexts can cross over a GO boundary (transactions and session-level temp tables are about it).
However, because both stored procedures and Dynamic SQL establish their own separate batches/execution contexts, you can use these to get around the normal need for GO, like so:
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
'

The word GO is a batch separator and is not a SQL keyword. In SSMS you can go to options and change it to anything - COME for example.
Try this:
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;'

Just stop using GO, it's not necessary (and it's not even T-SQL; it's just a batch separator for Management Studio).
Stop using sp_MSForEachDB. Oh, the problems (if you want proof, see here, here and here).
Here is code that does this without using any stored procedure:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += N'USE ' + QUOTENAME(name) + ';
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts];
EXEC sys.sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts'';
-- actually should use ALTER ROLE now
'
FROM sys.databases
WHERE state = 0 -- online
AND name LIKE N'MyDBName%';
PRINT #sql;
-- EXEC sys.sp_executesql #sql;

Like #RBarryYoung's answer, an embedded exec() can be used to avoid needing a "go" when the command carries the requirement that it must be the "first within a batch." I don't think the question's example needs a "go" but the headline itself may lead people here.
Here is a basic SP to wrap this that support's DDL statements like "create proc" and provides some error messages. I'm probably the ~5th person to do this and this one isn't the end all.
Blessing/Curse: There is a call to Common.ufn_UsvToNVarcharKeyTable() which you may need to patch in your own CSV splitter or remove:
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
Usage:
declare
#sql nvarchar(max) = N'
create proc usp_ReplaceEachSingleQuoteWithTwoSingleQuotesInTheDefinition
...';
exec [Common].[usp_ForEachDatabase]
#pSql = #sql,
#pDatabaseNameLikeOpt = null,
#pDatabaseNameCsvOpt = null,
#pJustPrintDbNames = 0,
#pDebug = 1;

Why not use a cursor?
They are great for management tasks like this. As long as the record set is small, they perform well.
The code below creates a SQL statement for each database that uses the ; to combine multiple tasks.
Unlike the code above, it remove any existing users before creating a new one. Avoids a possible issue.
Then it executes the code. If you want, you could even log these adhoc requests to a internal table and add error checking.
Good luck.
--
-- 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;

Related

Can I use a drop statement to delete specific principals from database after completing a restore from Production in SQL Server?

I have task to automate the process of taking a copy of a specific production environment and using the .bak file to restore a different environment (TEST or DEV) so that they don't end up out of sync. I understand this isn't exactly best practice, but it's the best solution right now for the problem we're addressing.
I obviously don't want to keep the principals/roles/permissions that my Prod environment has in my Test environment, and so I am looking for a way to delete the principals and then put the original test ones back.
I found Kenneth Fisher's example here. However, I'm wondering if that solution is a little over engineered for my purposes.
Is there a reason I shouldn't just use a script like below to delete exactly which principals I need to from the restored test file and then use Mr. Fisher's script to put the old principals back?
DECLARE #sql AS NVARCHAR(500)
DECLARE #name sysname
DECLARE cursor_name CURSOR FAST_FORWARD READ_ONLY FOR
SELECT name
FROM sys.database_principals
WHERE [type] in (N'U',N'S',N'G')
AND name NOT IN ('dbo', 'guest', 'INFORMATION_SCHEMA', 'sys', 'public', 'codaprod_user', 'NT SERVICE\HealthService')
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = N'IF EXISTS (SELECT TOP 1 1 FROM sys.schemas WHERE [name] = ''' + #name + ''')
BEGIN
DROP SCHEMA [' + #name + N']
END
BEGIN TRY
DROP USER [' + #name + N']
END TRY
BEGIN CATCH END CATCH;'
--PRINT 'USE ' + DB_NAME() + ';' + #sql
EXEC sys.sp_executesql #sql
FETCH NEXT FROM cursor_name INTO #name
END
CLOSE cursor_name
DEALLOCATE cursor_name;
GO

Drop databases with no tables

Is there a query which drops / deletes databases with no tables in them (deletes empty databases)?
Server is Microsoft SQL Server 2005
This should do it.
Tested on a lab machine and it dropped all databases with 0 user tables.
Note, however, that tables aren't the only things in a database, necessarily. There could be stored procedures, functions, etc that someone might still need.
NOTE THAT THIS IS A VERY DANGEROUS OPERATION, AS IT DROPS DATABASES. USE AT YOUR OWN RISK. I AM NOT RESPONSIBLE FOR DAMAGE YOU CAUSE.
USE [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery = 'USE [' + #name + ']; IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
DROP DATABASE [' + #name + ']
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
Note also that, if a database is in use, SQL Server will refuse to drop it. So, if there are other connections to a particular database that this tries to drop, the command will abort.
To avoid that problem, you can set the database in question to single-user mode.
The following script is the same as the above, except it also sets the target databases to single-user mode to kill active connections.
BE EVEN MORE CAREFUL WITH THIS, AS IT'S ESSENTIALLY THE NUCLEAR OPTION:
use [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery =
'USE [' + #name + '];
IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
ALTER DATABASE [' + #name + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [' + #name + '];
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
I think it is not possible (at least not with a TSQL command, might be a stored procedure somewhere). You would have to query sysobjects and abort if any found. (And you have to decide if you want to ignore some of the system objects like the design tables).
The script below will generate the needed DROP DATABASE script. You could tweak this to execute the statement too (in the master database context) but I suggest you review it first just in case.
EXEC sp_MSforeachdb N'
USE [?];
IF N''?'' NOT IN(N''master'', N''model'', N''msdb'', N''tempdb'')
BEGIN
IF NOT EXISTS(
SELECT *
FROM sys.tables
WHERE
OBJECTPROPERTYEX(object_id, ''IsMSShipped'') = 0
)
BEGIN
PRINT ''DROP DATABASE [?];'';
END;
END;';

T-SQL Stored Procedure with Cursor Loop causing Errors

Could someone please explain to me why I have error message show up when I exec the stored procedure. "A cursor with the name 'tName_cursor' already exists"
DECLARE #tName VARCHAR(100)
DECLARE #lsql VARCHAR(8000)
DECLARE tName_cursor CURSOR FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
OPEN tName_cursor;
FETCH NEXT FROM tName_cursor INTO #tName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lsql = N'
UPDATE [' +#tName+ ']
SET LATESTOFALL_FLG = ''N''
FROM [' +#tName+ '] T
JOIN D_CUSTOM_METRICS_RULE S
ON T.METRIC_ID = S.CUSTOM_METRIC_RULE_ID
AND T.LATESTOFALL_FLG = ''Y'''
EXECUTE sp_executesql #lsql
FETCH NEXT FROM tName_cursor INTO #tName;
END
CLOSE tName_cursor;
DEALLOCATE tName_cursor;
DECLARE tName_cursor_REDO CURSOR FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
OPEN tName_cursor_REDO;
FETCH NEXT FROM tName_cursor_REDO INTO #tName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lsql = N'
UPDATE [' +#tName+ ']
SET LATESTOFDAY_FLG = ''N''
FROM [' +#tName+ '] T
JOIN D_CUSTOM_METRICS_RULE S
ON T.METRIC_ID = S.CUSTOM_METRIC_RULE_ID
AND T.CALC_METRIC_DATE_ID = CONVERT(INT,convert(VARCHAR, getdate(), 112))
AND T.LATESTOFDAY_FLG = ''Y'''
EXECUTE sp_executesql #lsql
FETCH NEXT FROM tName_cursor_REDO INTO #tName;
END
CLOSE tName_cursor_REDO;
DEALLOCATE tName_cursor_REDO;
Looks like some exception or 'return' breaks execution before DEALLOCATE command
"A cursor with the name 'tName_cursor' already exists"
message can happen when your 'declare' block is executed successfully, but after some code fails before 'DEALLOCATE tName_cursor' statement. Then, the second time you execute stored proc, it tries to declare a cursor again and the error message pops out. I recommend you to add 'begin try .. end try' block to your code and 'print ERROR_MESSAGE()' in exception block to see what comes out.
It sounds like you may be using GLOBAL cursors (?).
If you don't need them, I suggest using LOCAL cursors.
e.g.
DECLARE tName_cursor_REDO CURSOR LOCAL FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
That may help to make things more robust.

get sql log file name from within a script

We have a few test databases that we are throwing test indexes into and the log file gets bloated real quickly due to us dropping the contents of the table and repopulating it.
I have found, thanks to Stack Overflow, a few scripts and put them together to do what I need.
Here is the script:
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DBCC SHRINKFILE(N'SSSIndexes_Log', 1) <-- my issue is here
GO
The problem is the log file name. Is there a way to get the log file name without having to look it up manually and include it in the script to where this part is automated?
Btw, we never intend on restore this database. These are temporary indexes.
Thanks!
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
You can use this to generate script for log file truncating of all databases on a specific server. To stick to specific databases use a filter.
SELECT
' USE [' + name + ']; GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE [' + name + '] SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DECLARE #logname varchar(128);
SELECT TOP 1 #logname=[name] FROM [' + name + '].[sys].[database_files] WHERE [type] = 1;
DBCC SHRINKFILE (#logname, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE [' + name + '] SET RECOVERY FULL;
GO
' AS qry FROM master.dbo.sysdatabases
WHERE dbid > 6
WARNING!!!: YOU SHOULD ONLY DO THIS ON TEST DB SERVERS. Production DBs typically WANT to have FULL recovery mode. Test DBs you usually don't care.
Expanding on Abdul's answer (I had trouble getting it to show in new lines) and Dis' answers this finds the first db that has recovery FULL, sets it to simple and shrinks it... just keep clicking execute until it returns null
declare #dbname nvarchar(50)
declare #logfilename nvarchar(100)
select top(1) #dbname = name from sys.databases where recovery_model_desc <> 'SIMPLE' AND name <> 'tempdb'
declare #shrinkdb nvarchar(500)
set #shrinkdb = 'USE master
ALTER DATABASE [' + #dbname + '] SET RECOVERY SIMPLE
'
select #shrinkdb
execute sp_executesql #shrinkdb
set #shrinkdb = 'USE [' + #dbname + ']
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
'
select #shrinkdb
execute sp_executesql #shrinkdb

How to execute T-SQL for several databases whose names are stored in a table?

I have several databases (SqlServer 2005) on the same server with the same schema but different data.
I have one extra database which has one table storing the names of the mentioned databases.
So what I need to do is to iterate over those databases name and actually "switch" to each one (use [dbname]) and execute a T-SQL script. Am I clear?
Let me give you an example (simplified from the real one):
CREATE TABLE DatabaseNames
(
Id int,
Name varchar(50)
)
INSERT INTO DatabaseNames SELECT 'DatabaseA'
INSERT INTO DatabaseNames SELECT 'DatabaseB'
INSERT INTO DatabaseNames SELECT 'DatabaseC'
Assume that DatabaseA, DatabaseB and DatabaseC are real existing databases.
So let's say I need to create a new SP on those DBs. I need some script that loops over those databases and executes the T-SQL script I specify (maybe stored on a varchar variable or wherever).
Any ideas?
The simplest way is this:
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
However, obviously it will not work for statements that need to be the first statement in a batch, like CREATE PROCEDURE. For that you can use SQLCLR. Create and deploy a class like this:
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();
}
}
}
}
Then you can do
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
I guess this will generally not be possible in TSQL, since, as others pointed out,
you first need as USE statement to change the database,
followed by the statement you want to execute, which is, although not specified, a DDL statement which must be first in a batch.
Moreover, you cannot have a GO in a string to be EXECuted.
I found a command-line solution invoking 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()"
Sample code uses current Windows login to query all active databases from Master (replace with your own connection and query for databases), and executes a literal TSQL command on each database thus found. (line breaks for clarity only)
Have a look at the command-line parameters of sqlcmd. You can pass it a TSQL file as well.
If you want to allow manual selection of databases, have a look at SSMS Tools Pack.
You should be able to do this with the sp_MSforeachdb undocumented stored procedure.
This method requires you to put your SQL script to be executed on each DB in a variable, but should work.
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
Also, as some have pointed out. This approach is problematic if you want to run a command that needs to be the only thing in a batch.
Here is an alternative for that situation, but it requires more permissions than many DBA's might want you to have and requires you to put your SQL into a separate text file.
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
Use the USE command and repeat your commands
Ps. Have a look at how to use USE with a parameter here
I know this question is 5 years old, but I found this via Google, so others may as well.
I recommend sp_msforeachdb system stored procedure. You do not need to create any other stored procedures or cursors.
Given your table of database names is already created:
EXECUTE sp_msforeachdb '
USE ?
IF DB_NAME()
IN( SELECT name DatabaseNames )
BEGIN
SELECT
''?'' as 'Database Name'
, COUNT(*)
FROM
MyTableName
;
END
'
I do this to summarize counts in many databases I have restored from several different sites with the same installed database schema.
Example:
-- Repeat the execution of SQL Commands across all site archived databases.
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
'
The trickiest part are the single quotes '