I got a request from my manager to update a document we have here called our Service Catalog. The document is published by the IT department and lists all the major software programs we use, the business units that use them, whether or not they are 24x7 systems, and some other stats about our department. My job was to update the database section.

If you can guess how many objects are in the database, you win the database!

One of the items was the number of SQL Servers we have and their versions. This was relatively easy. The next piece of information I needed to update was the total number of databases we have. A bit trickier, but since I have my Drive Space Monitoring procedure in place, and that breaks out sizes by database, it was a simple matter to write a query to count the total number of databases we have.

The last piece of information I needed to provide was a count of "database objects." Huh? I have no idea what this meant, and neither did my manager. Apparently it was put in the document by a DBA who has long since left. The size of the number didn't really give me any clues either. We had about 600 database and the database object number was just over 7,000.

To me, a database object refers to something very specific - something that is stored in a database (excluding user data). So that would include all tables, views, stored procedures, triggers, foreign keys, constraints, logins, etc. If that was truly what this number represented, a value of 7,000 for 600 databases seems very low to me. Furthermore, I'm not even sure why this number would be in the document, unless it was to make it sound like the DBA had a huge domain to watch over.

Well, whatever the case, I decided to go ahead and update the number, although I will be adding a note to the document indicating the number represents the total number of user tables, views, and stored procedures in our environment.

The next step was determining the easiest way to get that data. I do not have a Central Configuration Server set up, which probably would have made this job a bit easier. So instead, I decided to use the multi-server query capabilities of SSMS. A slight problem was that my environment has a mix of SQL 2000, 2005, and 2008 R2 servers, so the basic system tables I would need to query would be slightly different. No problem. I've written before about how I like to write my scripts to be version agnostic. Well, they aren't truly agnostic in that they don't care about which version they are running on, but they do detect the version and then use the appropriate tables. By writing scripts this way, I can execute the same script in SSMS against multiple servers at once and not get errors.

Below is my code to count all user tables, views, and stored procedures in all user databases on a server. Note that is skips system databases and offline databases. If you want to count things besides tables, views, and stored procedures, simply add them to the CASE and WHERE statements in the definition of the @SQL variable. This script will work on 2000, 2005, 2008, and 2008 R2 servers. It will probably even work on SQL 7.0 servers, but you'll need to change the IF statement that looks at the ProductVersion property.

 


/*
Script to count all user tables, views, and stored procedures in
	all user databases on a server.

shaunjstuart.com

Verion 1.0

*/

DECLARE @DBName AS VARCHAR(100)
DECLARE @TotalUserTables INT
DECLARE @TotalStoredProcedures INT
DECLARE @TotalViews INT

CREATE TABLE #tblDBResults
        (
         DatabaseName VARCHAR(100)
        ,Item VARCHAR(50)
        ,Total_count INT
        )

DECLARE @SQL VARCHAR(1000)
	/* basic select statement */
DECLARE @SQLWithDB VARCHAR(1000)
	/* select statement with USE db added */

SET @TotalUserTables = 0
SET @TotalStoredProcedures = 0
SET @TotalViews = 0

/* Use appropriate tables based on SQL Server version */
/* 8 = SQL 2000, otherwise assume 2005+ */

IF (SELECT  LEFT(CAST(SERVERPROPERTY('ProductVersion') AS CHAR(1)), 1)
   ) = 8
   BEGIN

-- Configure basic select statement (without 'USE db' part) for SQL 2000 servers

         SET @SQL = 'SELECT	DB_NAME(),
							[type]
						   ,total_count = COUNT(*)
					FROM    (SELECT type = CASE WHEN xtype = ''U'' THEN ''User tables''
												WHEN xtype = ''V'' THEN ''Views''
												WHEN xtype = ''P'' THEN ''Stored procs''
										   END
							 FROM   sysobjects
							 WHERE  xtype IN (''P'', ''U'', ''V'')
							) s
					GROUP BY type;'

         DECLARE DatabasesCursor CURSOR
         FOR
                 SELECT name
                 FROM   sysdatabases
                 WHERE  dbid > 4
                        AND (512 & status) <> 512
					/* exclude system and offline databases */
   END
ELSE
   BEGIN

-- Configure basic select statement (without 'USE db' part) for SQL 2005+ servers

         SET @SQL = 'SELECT	DB_NAME(),
					[type]
                   ,total_count = COUNT(*)
            FROM    (SELECT type = CASE WHEN type = ''U'' THEN ''User tables''
                                        WHEN type = ''V'' THEN ''Views''
                                        WHEN type = ''P'' THEN ''Stored procs''
                                   END
                     FROM   sys.objects
                     WHERE  type IN (''P'', ''U'', ''V'')
                    ) s
            GROUP BY type;'

         DECLARE DatabasesCursor CURSOR
         FOR
                 SELECT name
                 FROM   sys.databases
                 WHERE  Database_id > 4
                        AND sys.databases.state = 0
				/* exclude system and offline databases */
   END

OPEN DatabasesCursor
FETCH NEXT FROM DatabasesCursor INTO @DBName

WHILE @@fetch_status = 0
      BEGIN

            SET @SQLWithDB = 'USE [' + @DBName + '];' + CHAR(10) + CHAR(13)
                + @SQL

            INSERT  #tblDBResults
                    EXEC (@SQLWithDB)

            FETCH NEXT FROM DatabasesCursor INTO @DBName

      END

CLOSE DatabasesCursor
DEALLOCATE DatabasesCursor

SELECT  Item
       ,SUM(Total_count) AS TotalItems
FROM    #tblDBResults
GROUP BY [#tblDBResults].Item
ORDER BY [#tblDBResults].Item

DROP TABLE #tblDBResults

Unfortunately, if you are running this across multiple servers using the SSMS trick I mentioned earlier, there is one additional step you need to do. SSMS, when performing a query against multiple servers, will add a column to the result set called Server Name. Your result set will include a set of numbers for each server. There is no way to have SSMS aggregate these into one set of totals. You'll need to copy and paste the results into Excel and sum up the numbers of tables, views, and stored procedures across all servers.

By the way, counted this way, my count of database objects went from just over 7,000 to over 550,000, even while the number of databases dropped by 150.  Restricting the count to only user tables, the number was still over 250,000. Clearly the old value was looking at something else. But since no one knows what it was, I'm going to redefine the term and do things my way.

 

Share