![]() |
|
|
|
By: Tim Ford | Read Comments (26) | Related Tips: 1 | 2 | 3 | More > Scripts |
Problem
There are times when I find myself needing to run a SQL command against each database on one of my SQL Server instances. There is a handy undocumented stored procedure that allows you to do this without needing to set up a cursor against your sysdatabases table in the master database: sp_MSforeachdb.
Solution
The syntax for this undocumented procedure is:
EXEC sp_MSforeachdb @command
(Where @command is a variable-length string.)
Example 1: Query Information From All Databases On A SQL Instance
| --This query will return a listing of all tables in all databases on a SQL instance: DECLARE @command varchar(1000) SELECT @command = 'USE ? SELECT name FROM sysobjects WHERE xtype = ''U'' ORDER BY name' EXEC sp_MSforeachdb @command |
You can alternately omit the process of declaring and setting the @command variable. The T-SQL command below behaves identically to the one above and is condensed to a single line of code:
| --This query will return a listing of all tables in all databases on a SQL instance: EXEC sp_MSforeachdb 'USE ? SELECT name FROM sysobjects WHERE xtype = ''U'' ORDER BY name' |
Example 2: Execute A DDL Query Against All User Databases On A SQL Instance
| --This statement creates a stored procedure in each user database that will return a listing of all users in a database, sorted by their modification date
DECLARE @command varchar(1000) SELECT @command = 'IF ''?'' NOT IN(''master'', ''model'', ''msdb'', ''tempdb'') BEGIN USE ? EXEC(''CREATE PROCEDURE pNewProcedure1 AS SELECT name, createdate, updatedate FROM sys.sysusers ORDER BY updatedate DESC'') END' EXEC sp_MSforeachdb @command |
As you may notice, there are additional items to take into consideration when limiting the scope of the sp_MSforeachdb stored procedure, particularly when creating or modifying objects. You must also set the code to execute if the IF statement is true by using the T-SQL keywords BEGIN and END. You should take note that the USE ? statement is contained within the BEGIN...END block. It is important to remember key T-SQL rules and account for them. In this case the rule that when creating a procedure, the CREATE PROCEDURE phrase must be the first line of code to be executed. To accomplish this you can encapsulate the CREATE PROCEDURE code within an explicit EXEC() function.
What about the "?" Placeholder
Throughout the examples provided above you'll see the use of the question mark as a placeholder for the database/database name. To reference the database name as a string to be returned in a query, embed it between a double set of single quotation marks. To treat it as a reference to the database object simply use it by itself (as presented in Example 3b.) It is necessary to set the database for the query to run against, by using the USE ? statement, otherwise the code will execute in the context of the current database, for each database in your SQL instance. If you have 5 databases hosted in the current instance and you were to run the stored procedure code above while in the context of DBx it would execute the T-SQL text of the @command 5 times in DBx. This behavior is evident in the output of Example 3 below.
Example 3: Query File Information From All Databases On A SQL Instance
| --This query will return a listing of all files in all databases on a SQL instance: EXEC sp_MSforeachdb 'USE ? SELECT ''?'', SF.filename, SF.size FROM sys.sysfiles SF' |

What happens though if we omit the USE ? clause, which sets the scope of the query? As you can see below, though it is apparent the code executed for each database, it never changed context. Pay particular interest to the filename column and you will see that the query executed from within the context of the master database (where I ran the query from) for each database in the SQL instance (as noted by the database name being returned via the use of the "?" placeholder).
| --Remove the USE ? clause and you end up executing the query repetitively within the context of the current database:
EXEC sp_MSforeachdb 'SELECT ''?'', SF.filename, SF.size FROM sys.sysfiles SF' |

Why Not Just Use a Cursor?
Sure, a cursor can accomplish all that I've presented above, but let's look at the code required to set up a cursor to execute the command used in Example 3:
| DECLARE @DB_Name varchar(100) DECLARE @Command nvarchar(200) DECLARE database_cursor CURSOR FOR OPEN database_cursor FETCH NEXT FROM database_cursor INTO @DB_Name WHILE @@FETCH_STATUS = 0 FETCH NEXT FROM database_cursor INTO @DB_Name CLOSE database_cursor |
Considering the behavior is similar I'd rather type and execute a single line of T-SQL code versus sixteen.
Next Steps
| Thursday, January 17, 2008 - 5:23:02 PM - probaby | Read The Tip |
|
Thanks for your wonderful scripts! You save my time and money! Have a great day!
|
|
| Thursday, April 03, 2008 - 11:53:47 AM - prasanthg74 | Read The Tip |
|
nice script ! thanks a bunch! |
|
| Friday, April 04, 2008 - 7:07:46 AM - admin | Read The Tip |
|
Thanks so much. Tim Ford did a great job with this tip. Thank you, |
|
| Wednesday, December 10, 2008 - 3:23:23 PM - robvon | Read The Tip |
|
You may need some "[" and "]" around the '?' to deal with dodgy database names....... as in:EXEC sp_MSforeachdb 'USE [?] SELECT ''?'', SF.filename, SF.size FROM sys.sysfiles SF'R |
|
| Sunday, December 14, 2008 - 9:04:32 PM - timmer26 | Read The Tip |
|
You are absolutely correct. This is particularly important when dealing with spaces in database names. |
|
| Friday, November 20, 2009 - 3:49:19 AM - bobmihada | Read The Tip |
| --// Just another flavor to offer IF OBJECT_ID('tempdb..##TempDBInfo') IS NOT NULL BEGIN PRINT 'Previous ##TmpDBInfo found and Dropped.' DROP TABLE ##TempDBInfo END CREATE TABLE ##TempDBInfo(DBName nVarChar(255), FileName nvarchar(255), fileSize INT) EXEC sp_MSforeachdb 'USE [?] INSERT INTO ##TempDBInfo SELECT ''?'', SF.filename, SF.size FROM sys.sysfiles SF' SELECT * FROM ##TempDBInfo --// DROP TABLE ##TempDBInfo | |
| Wednesday, January 27, 2010 - 11:19:23 AM - Akbar | Read The Tip |
|
Hi All,
Is there are way to grant Execute permission to a particular Stored procedure or Function in all database of SQL Server 2008 Instance?
The below one use to Grant Execute permission to any procedure. EXEC sp_msforeachdb 'USE ?; GRANT EXECUTE TO UserOrRoleName' But when trying the below one I'm getting error EXEC sp_msforeachdb 'USE ?; GRANT EXECUTE ON [?].[dbo].[sp_StoredProc1] TO UserOrRoleName' Error Message: Cannot find the object 'sp_StoredProc1', because it does not exist or you do not have permission. I have the 'sp_StroedProc1' exist in all my database in the sql server 2008 instance. Any help will be highly appreciated. |
|
| Friday, January 29, 2010 - 12:02:55 PM - aprato | Read The Tip |
|
|
|
| Monday, April 26, 2010 - 3:02:40 PM - thomashmaine | Read The Tip |
|
is there a way to have a variabled declared withing the statement?
select @command1 = 'use [?]; exec sp_grantlogin [@user2]'exec sp_MSforeachdb @command1 |
|
| Wednesday, November 03, 2010 - 10:11:28 AM - Pradip Patel | Read The Tip |
|
You need to replace SELECT @Command = 'SELECT ' + '''' + @DB_Name + '''' + ', SF.filename, SF.size FROM sys.sysfiles SF' SELECT @Command = 'USE '+ @DB_NAME + ' SELECT ' + '''' + @DB_Name + '''' + ', SF.filename, SF.size FROM sys.sysfiles SF' when you want to execute by cursor otherwise it gives you all file name as master.mdf
|
|
| Tuesday, February 01, 2011 - 11:49:50 AM - Larrybud | Read The Tip |
|
There are times when you might need a cursor: 1) running a script against certain databases, instead of all 2) wanting your script results in one table (for a select, for example)
|
|
| Thursday, September 08, 2011 - 4:16:37 PM - Jason | Read The Tip |
|
When you need a special condition of list of DBs such as not offline, not read-only, use temp table or CTE or table variable, for example, the code below Regards, Jason
----------------- SET NOCOUNT ON DECLARE @dbName varchar(255), @rc int SELECT name SELECT @rc = 1, @dbName = MIN(name) WHILE @rc <> 0 EXEC DB.schema.DO_SOMETHING
SET @rc = @@ROWCOUNT DROP TABLE #db |
|
| Tuesday, February 07, 2012 - 8:49:37 AM - Fernando Paez Becker | Read The Tip |
|
There is anothers parameters to comments... EXEC sp_msforeachdb sintax accepts EXEC sp_msforeachdb @precommand='---YOUR TEXT FOR GENERAL PURPUSE HERE-TEMP TABLE CREATION IE... CREATE TABLE ##TEMP1 (ID INT,NAME VARCHAR(15);SELECT * INTO ##TEMP2 FROM ##TEMP1', @COMMAND1='USE [?];SELECT * INTO ##TEMP1 WHERE ...', @COMMAND2='USE [?];SELECT * INTO ##TEMP2 WHERE ...', @COMMAND3='USE [?];SELECT * INTO ##TEMP1 WHERE ...', @POSTCOMMAND='SELECT * FROM ##TEMP1 UNION SELECT * FROM ##TEMP2;DROP TABLE ##TEMP1;DROP TABLE ##TEMP2;'
|
|
| Tuesday, February 28, 2012 - 4:31:02 AM - akash | Read The Tip |
|
DECLARE @DB_Name varchar(100) OPEN database_cursor FETCH NEXT FROM database_cursor INTO @DB_Name WHILE @@FETCH_STATUS = 0 SELECT @Command = 'USE ['+ @DB_NAME + '] INSERT INTO genMenuMaster FETCH NEXT FROM database_cursor INTO @DB_Name CLOSE database_cursor
|
|
| Tuesday, February 28, 2012 - 4:31:43 AM - akash | Read The Tip |
|
Server: Msg 105, Level 15, State 1, Line 3 |
|
| Tuesday, March 13, 2012 - 11:35:27 PM - surajkuamwat | Read The Tip |
|
Respect sir i am facing some problem in sql query using cursor declaration t was try to do with declare variable
declare
cmain cursorforselect*from gstock
open
cmain how to use cursor in fetch status...
|
|
| Tuesday, March 13, 2012 - 11:54:24 PM - SURAJ | Read The Tip |
|
Dear sir I AM FACING SOME PROBLEM IN SQL QUERY CURSOR DECLARATION I WANT TO USE CURSOR WITHOUT DECLARE VARIABLE PLEASE GIVE ME SOLUCTION MENTON MY QUERY DECLARE @ABCTABLE AS TABLE (STKCODE INT,STOCKNO VARCHAR(20)) declare abc_cursor cursor for select stkcode ,stockno from stock OPEN abc_curosr ** THIS QUERY EXECUTE SUCESSFUL BUT HOW TO USE IN FETCH NEXT FROM CURSOR FETCH NEXT FROM abc_curosr INTO @stkcode,@stockno ** ERROR CURSOR VARIABLE NOT READ WHILE @@FETCH_STATUS = 0 INSET INTO @ABCTABLE (STKCODE,STOCKNO) VALUES (@stkcode,@stockno) FETCH NEXT FROM abc_curosr INTO @DB_Name CLOSE abc_curosr |
|
| Tuesday, May 01, 2012 - 6:27:28 PM - corey_lawson | Read The Tip |
|
This, and sp_msforeachtable, are internally not magic or undocumented, in that they don't invoke any CLR functionality, extended stored procs, obfuscated TSQL, etc. They're just plain ol' TSQL procs that executes the code specified in the parameters inside a CURSOR that loops over the desired objects...
|
|
| Wednesday, May 16, 2012 - 6:40:50 AM - Sandeep Maher | Read The Tip |
|
Hi, Kindly help me.. Thanks in Advance. |
|
| Monday, June 18, 2012 - 5:32:37 PM - Ludwig Guevara | Read The Tip |
|
Hi, I use this script to fix all Orphan users in a Server (Use a cursor, because requires to check for all the users in the database) exec sp_msforeachdb ' Thanks for the tip Regards |
|
| Monday, June 18, 2012 - 5:34:53 PM - Ludwig Guevara | Read The Tip |
|
Hi, I also use a simple script (Callit from a Job) to check for the status of all the databases. Runs after a Failover or a Server Reboot.
CREATE PROC [dbo].[usp_CheckDB] |
|
| Tuesday, June 19, 2012 - 1:55:45 PM - Kevin Archibald | Read The Tip |
|
Nice! I use this to shrink db and truncate logs of EVERY DB.
DECLARE @cmd1 nvarchar(2000) SET @cmd1 = 'IF ''?'' NOT IN(''master'', ''model'', ''tempdb'', ''msdb'')' + 'BEGIN ' + 'USE ? DBCC SHRINKFILE( ?_log, 1 ) BACKUP LOG ? WITH TRUNCATE_ONLY DBCC SHRINKFILE( ?_log, 1 ) END '
EXEC sp_MSForEachdb @command1 = @cmd1 GO |
|
| Friday, August 03, 2012 - 7:14:31 AM - Amol Birar | Read The Tip |
|
Very useful indeed |
|
| Tuesday, December 04, 2012 - 6:28:32 AM - Jordan | Read The Tip |
|
Thanks a lot man ! You save my day. Regards, Jordan |
|
| Wednesday, December 26, 2012 - 3:13:45 PM - Christina | Read The Tip |
|
I need to loop the following script through all databases. I have tried almost everything I have researched online without any success. Any suggestions?
DECLARE @table_name VARCHAR(500)
DECLARE @schema_name VARCHAR(500) DECLARE @tab1 TABLE( tablename VARCHAR (500) collate database_default ,schemaname VARCHAR(500) collate database_default ) CREATE TABLE #temp_Table ( [tablename] [sysname] NOT NULL, [row_count] [int] NULL, [reserved] [varchar](50) NULL, [data] [varchar](50) NULL, [index_size] [varchar](50) NULL, [unused] [varchar](50) NULL)
INSERT INTO @tab1 SELECT Table_Name, Table_Schema FROM information_schema.tables WHERE TABLE_TYPE = 'BASE TABLE'
DECLARE c1 CURSOR FOR SELECT Table_Schema + '.' + Table_Name FROM information_schema.tables t1 WHERE TABLE_TYPE = 'BASE TABLE'
OPEN c1 FETCH NEXT FROM c1 INTO @table_name WHILE @@FETCH_STATUS = 0 BEGIN SET @table_name = REPLACE(@table_name, '[',''); SET @table_name = REPLACE(@table_name, ']','');
-- make sure the object exists before calling sp_spacedused IF EXISTS(SELECT id FROM sysobjects WHERE id = OBJECT_ID(@table_name)) BEGIN INSERT INTO #temp_Table EXEC sp_spaceused @table_name, false; END
FETCH NEXT FROM c1 INTO @table_name END CLOSE c1 DEALLOCATE c1
SELECT t1.* ,t2.schemaname FROM #temp_Table t1 INNER JOIN @tab1 t2 ON (t1.tablename = t2.tablename ) ORDER BY schemaname,t1.tablename;
ALTER TABLE #temp_Table ADD [DATE] DATE UPDATE #temp_Table SET [DATE] = CURRENT_TIMESTAMP
AlteR TABLE #temp_Table ADD [DATABASE] varchar (100) UPDATE #temp_Table SET [DATABASE] = DB_NAME()
CREATE TABLE #temp_Table_test (name varchar(100), data_compression tinyint, data_compression_desc varchar(100))
insert into #temp_Table_test SELECT T.name, P.data_compression, P.data_compression_desc FROM SYS.TABLES T, SYS.partitions P WHERE T.OBJECT_ID = P.OBJECT_ID GROUP BY T.name, P.data_compression, P.data_compression_desc
ALTER TABLE #temp_Table ADD [COMPRESSED] VARCHAR (50) UPDATE #temp_Table set [COMPRESSED] = b.data_compression_desc from #temp_Table a, #temp_Table_test b where a.tablename = b.name
---------------------------------------------- insert into bic_reference.dbo.test_database_stats select * from #temp_Table
DROP TABLE #temp_Table Drop Table #temp_Table_test
|
|
| Wednesday, March 06, 2013 - 4:40:16 PM - newbie | Read The Tip |
|
I need assisance, please. I am using the sp_msForcEachDB and it is having difficulities. 1) although, I have the filter on the databases excluding master, model, msdb, tempdb, it is still executing the script in them. 2) when the script executes, it only seems to get a few of the databases and not all of them. 3) I have also tried putting the script into a variable and running the exec sp_msforEachDB @command What am I doing wrong? I am testing the script as a print to be sure it is working before I run the execute. Yes, this is a cursor, I realize you are all going to 'smack' me on that also. The purpose of the script is to reorg indexes that are fragmented between 5 and 30 percent. Here is my script... I have included the output at the bottom.
EXEC
sp_MSforeachdb
'IF ''[?]'' NOT IN (''master'',''tempDB'',''model'',''msdb'') BEGIN
use [?]
DECLARE @dbName sysname,@table varchar(50), @index varchar(50), @frag int, @msg varchar(500),
@sql nvarchar(500), @dbsql nvarchar(500)
declare index_cur cursor for
SELECT OBJECT_NAME(object_id) AS tblName
,[?].dbo.index_name(object_id, index_id) AS ixName
,avg_fragmentation_in_percent
FROM [?].sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL)
WHERE avg_fragmentation_in_percent between 5 and 30
and index_type_desc IN(''CLUSTERED INDEX'', ''NONCLUSTERED INDEX'')
open index_cur
FETCH NEXT FROM index_cur into @table, @index, @frag
WHILE @@FETCH_STATUS = 0
BEGIN
if objectproperty (object_id (@table), ''OwnerId'') = 1
set @sql = N''ALTER INDEX '' + quotename(@index) + '' ON '' + ''[dbo].''+ quotename(@table)
+ '' REORGANIZE''
print @sql
FETCH NEXT FROM index_cur into @table, @index, @frag
END
CLOSE index_cur
DEALLOCATE index_cur
END'
Msg 4121, Level 16, State 1, Line 9
Cannot find either column "tempdb" or the user-defined function or aggregate "tempdb.dbo.index_name", or the name is ambiguous.
Msg 4121, Level 16, State 1, Line 9
Cannot find either column "model" or the user-defined function or aggregate "model.dbo.index_name", or the name is ambiguous.
Msg 4121, Level 16, State 1, Line 9
Cannot find either column "msdb" or the user-defined function or aggregate "msdb.dbo.index_name", or the name is ambiguous.
ALTER INDEX [IX_bs_unitanks_FloatTable_DateAndTime] ON [dbo].[BS_UNITANKS_FloatTable] REORGANIZE
ALTER INDEX [IX_beerfilter_FloatTable_DateAndTime] ON [dbo].[BeerFilter_FloatTable] REORGANIZE
ALTER INDEX [XPKItem] ON [dbo].[Item] REORGANIZE
|
|
|
privacy | disclaimer | copyright | advertise | about authors | contribute | feedback | giveaways | user groups Some names and products listed are the registered trademarks of their respective owners. Edgewood Solutions LLC | MSSharePointTips.com | MSSQLTips.com |