This tip presents two very useful stored procedures that live in the master database but are not mentioned in SQL Server Books Online. These system procedures come in very handy for jobs such as determining the space used, the number of rows, the indexes on the user tables, and so on.

The first procedure, sp_MSForEachDB, runs up to three commands against every database on the server of interest.

|> @command1: first command to execute
|> @replacechar: substitute another placeholder for the "?"
|> @command2: second command to execute
|> @command3: third command to execute
|> @precommand: execute command once before entering the loop
|> @postcommand: execute command once after exiting the loop

Each set of commands (even if the set consists of only one command) is run as a batch against the databases, so it's most useful to capture the output to text rather than to the standard result-set grid. To do this, select Query | Results To | Results To Text from the menu or press [Ctrl]T.

The following code returns the number of user tables in each database on the server:

exec sp_MSForEachDB
@command1 = "use ? exec sp_SpaceUsed"

The abbreviated output looks like this:

database_name database_size unallocated space
--------------------- ------------------ ------------------
master 5.25 MB 1.26 MB

reserved data index_size unused
------------------ ------------------ ------------------ ------------------
2808 KB 1144 KB 1080 KB 584 KB

The second procedure, sp_MSForEachTable, accepts seven parameters:

|> @command1: first command to execute
|> @replacechar: substitute another placeholder for the "?"
|> @command2: second command to execute
|> @command3: third command to execute
|> @whereand: Where condition (or Order By clause)
|> @precommand: execute command once before entering the loop
|> @postcommand: execute command once after exiting the loop

By naming the parameters you want to pass, you can skip the necessity of passing null values. The @replacechar parameter is useful when the commands you want to execute contain a question mark. The @whereand parameter was implemented so you can scope the output according to a filter.

You can also sneak in an ORDER BY clause. The following example returns the row counts from every table in the AdventureWorks database, and sorts them by table name:

exec sp_MSForEachTable
@command1 = "Print \'?\'"
, @command2 = "select count(*) from ?"
, @whereand = "ORDER BY 1"

Here are a few rows of the output:

[HumanResources].[Department]

-----------
16

[HumanResources].[Employee]

-----------
290

[HumanResources].[EmployeeAddress]


-----------
290

[HumanResources].[EmployeeDepartmentHistory]

-----------
296

I like that it sorts the tables by both schema and table name.

Database Jumping This was published in Database Jumping, check every Thursday for more stories

Related links

Comments

1

Tony White - 29/07/08

These procs have been around for quite a while, but do be aware that these are undocumented procs. As such they could be removed by Microsoft, without warning at any service pack or patch level.

» Report offensive content

2

Jamie - 29/07/08

*You could probably just recreate them yourself if they were removed and are critical.

I see commonly people throw the results in a temp table to combine into one result set. Something like

create table #blah( row_count_all_tables int )

insert #blah
exec sp_msforeachtable 'select count(*) from ?'

select sum(row_count_all_tables) from #blah
drop table #blah

» Report offensive content

3

Oded Dror - 29/07/08

I'm using the following view on a daily basis

SELECT     TOP (100) PERCENT a.name AS table_name, b.row_count
FROM sys.objects AS a INNER JOIN
sys.dm_db_partition_stats AS b ON a.object_id = b.object_id
WHERE (a.type = 'u') AND (b.index_id < 2)
ORDER BY b.row_count DESC

» Report offensive content

4

Tony - 29/07/08

Jamie, you are absolutely right.
Would it not be better though, to create your own procs to do just this within your DBA maintenance database? That way you will never lose them and can maintain some level of source control.
Even if you just crib some of MS code from the existing procs... After all, they are only cursors, which are, as we all know, the spawn of the devil - discuss :)

» Report offensive content

5

JumpIn - 30/07/08

WOW, Thanks a lot!

I've been using the MSForeach? procs for years but never knew the parameter names, didn't even know you could exec more than one query with a single call.

This is a great help. Again thanks a lot.

JumpIn

» Report offensive content

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

5

JumpIn - 30/07/08

WOW, Thanks a lot! I've been using the MSForeach? procs for years but never knew the parameter names, didn't even know you ... more

4

Tony - 29/07/08

Jamie, you are absolutely right. Would it not be better though, to create your own procs to do just this within ... more

3

Oded Dror - 29/07/08

I'm using the following view on a daily basis SELECT TOP (100) PERCENT a.name AS table_name, b.row_count FROM ... more

Log in


Sign up | Forgot your password?

  • Chris Duckett Safari gets Gears

    Since its release in May last year, Gears has supported only Internet Explorer and Firefox browsers. With the addition of Safari into the Gears fold, it closes the loop of major browsers to support Gears Read more »

    -- posted by Chris Duckett

  • Renai LeMay MyPerfect.com.au has potential

    Victorian Web start-up My Perfect has a strong story and rationale for why it will succeed. But it has to overcome some challenges and design flaws first. Read more »

    -- posted by Renai LeMay

  • Brendon Chase Blog against poverty

    Worldwide Blog Action Day is 15 October, in 2008 the goal is to raise awareness and conversation around the worldwide topic of poverty and in the process raise money for the cause. Who's in? Read more »

    -- posted by Brendon Chase

What's on?