﻿{"id":167,"date":"2014-10-10T11:31:24","date_gmt":"2014-10-10T11:31:24","guid":{"rendered":"http:\/\/www.codereview.co\/?p=167"},"modified":"2014-10-10T11:32:05","modified_gmt":"2014-10-10T11:32:05","slug":"sql-server-cursors-iterate-through-database","status":"publish","type":"post","link":"http:\/\/www.codereview.co\/index.php\/sql-server-tutorials\/sql-server-cursors-iterate-through-database\/","title":{"rendered":"SQL Server cursors: iterate through database"},"content":{"rendered":"<p><span style=\"color: #000000;\"><strong>Scenario Proposal<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\">There are times when you need to loop through all the databases or database objects to perform some tasks. For example you want to run a DBCC command against all the databases or take backups of all the databases on the server or you want to rebuild all the indexes of all the tables in the databases or you want to know the size of each table in a database. The simplest approach would be to create a cursor and loop through it, which requires you to write several lines of code. Is there any way to simplify the coding efforts for these kind of works?<\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Solution<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\">SQL Server has couple of undocumented system stored procedures, in the master database, which allow you to loop through all\/selected databases using sp_MSforeachdb system stored procedure or loop through all\/selected user tables using sp_MSforeachtable system stored procedure. You can even extend the functionality for views, stored procedures etc by using the sp_MSforeach_worker stored procedure, which is in fact used by the above two stored procedures as well.<\/span><\/p>\n<p><span style=\"color: #000000;\">These two system stored procedures use almost the similar set of parameters (more details in the below table) and return an integer value.<\/span><\/p>\n<table width=\"777\">\n<tbody>\n<tr>\n<td width=\"133\"><span style=\"color: #000000;\"><strong>Parameters<\/strong><\/span><\/td>\n<td width=\"96\"><span style=\"color: #000000;\"><strong>Type<\/strong><\/span><\/td>\n<td width=\"133\"><span style=\"color: #000000;\"><strong>sp_Msforeachtable<\/strong><\/span><\/td>\n<td width=\"117\"><span style=\"color: #000000;\"><strong>sp_Msforeachdb<\/strong><\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\"><strong>Description<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@precommand<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">This command is executed before any commands and can be used for setting up an environment for commands execution.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@command1<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">First command to be executed against each table\/database.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@command2<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">Second command to be executed against each table\/database.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@command3<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">Third command to be executed against each table\/database.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@postcommand<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">This command is executed after any other commands and can be used for cleanup process after commands execution.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@replacechar<\/span><\/td>\n<td><span style=\"color: #000000;\">nchar(1)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">Default value is &#8220;?&#8221; which represents the database\/table name. You may need to change this value if you want &#8220;?&#8221; mark to be used in your query.<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">@whereand<\/span><\/td>\n<td><span style=\"color: #000000;\">nvarchar(2000)<\/span><\/td>\n<td><span style=\"color: #000000;\">Yes<\/span><\/td>\n<td><span style=\"color: #000000;\">No<\/span><\/td>\n<td width=\"297\"><span style=\"color: #000000;\">With this you can specify the filtering criteria for your table collection. For details see the script section,<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #000000;\">Script #1 contains a simple script to demonstrate the use of sp_MSForEachTable stored procedure. The first script lists all the tables and total number of rows in the current database whereas the second script displays the space used by each table in the current database.<\/span><\/p>\n<table width=\"600\">\n<tbody>\n<tr>\n<td><span style=\"color: #000000;\"><strong>Script #1 : sp_MSForEachTable system stored procedure<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">&#8211;List all the tables of current database and total no rows in it EXEC sp_MSForEachTable &#8216;SELECT &#8221;?&#8221; as TableName, COUNT(1) as TotalRows FROM ? WITH(NOLOCK)&#8217; &#8211;List all the tables of current database and space used by it EXECUTE sp_MSforeachtable &#8216;EXECUTE sp_spaceused [?];&#8217;; GO<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #000000;\">Script #2 extends the usage of last script to use other parameters. This script creates a temporary table to hold the resultsets returned by the sp_spaceused stored procedure in the pre-execute phase. Then with @command1 it updates the statistics for all the tables and with @command2 it inserts the results to the temporary table created in the pre-execute phase. Further it narrows down the list of tables to consider, which is only tables belonging to HumanResources schema by using the @whereand parameter. Finally after execution of all these commands (during post execution) it selects records from the temporary table and then drops it.<\/span><\/p>\n<table width=\"600\">\n<tbody>\n<tr>\n<td><span style=\"color: #000000;\"><strong>Script #2 : sp_MSForEachTable system stored procedure<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">&#8211;Creates a temporary table to hold the resultsets &#8211;returned by sp_spaceused and before calling it, &#8211;it updates the statistics for each table &#8211;Filter out tables of HumanResources schema only EXECUTE sp_MSforeachtable @precommand = &#8216;CREATE TABLE ##Results ( name nvarchar(128), rows char(11), reserved varchar(50), data varchar(50), index_size varchar(50), unused varchar(50) )&#8217;, @command1 = &#8216;UPDATE STATISTICS ?;&#8217;, @command2 = &#8216;INSERT INTO ##Results EXECUTE sp_spaceused [?];&#8217;, @whereand = &#8216;and schema_name(schema_id) = &#8221;HumanResources&#8221;&#8217;, @postcommand = &#8216;SELECT * FROM ##Results; DROP TABLE ##Results&#8217; Go<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #000000;\">By default sp_MSForEachTable internally uses OBJECTPROPERTY(o.id, N&#8221;IsUserTable&#8221;) = 1 to consider only user tables. You can change this default behavior by using @whereand parameter to consider system tables or views or stored procedures or combination of these etc. For example in Script #3, the first script uses the last script as above and considers both user tables as well as system tables. In the second script it considers only views and displays its text likewise in last script it considers only stored procedures and displays its text.<\/span><\/p>\n<table width=\"600\">\n<tbody>\n<tr>\n<td><span style=\"color: #000000;\"><strong>Script #3 : sp_MSForEachTable system stored procedure<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">&#8211;Creates a temporary table to hold the resultsets &#8211;returned by sp_spaceused and before calling it, &#8211;it updates the statistics for each table &#8211;Note it consider both user and system tables EXECUTE sp_MSforeachtable @precommand = &#8216;CREATE TABLE ##Results ( name nvarchar(128), rows char(11), reserved varchar(50), data varchar(50), index_size varchar(50), unused varchar(50) )&#8217;, @command1 = &#8216;UPDATE STATISTICS ?;&#8217;, @command2 = &#8216;INSERT INTO ##Results EXECUTE sp_spaceused [?];&#8217;, @whereand = &#8216;or OBJECTPROPERTY(o.id, N&#8221;IsSystemTable&#8221;) = 1&#8217;, @postcommand = &#8216;SELECT * FROM ##Results; DROP TABLE ##Results&#8217; Go<\/span><span style=\"color: #000000;\">Use<\/span><span style=\"color: #000000;\">AdventureWorks GO &#8211;Display the views&#8217; script text EXECUTE sp_MSforeachtable @command1 = &#8216;sp_helptext [?];&#8217;, @whereand = &#8216;and OBJECTPROPERTY(o.id, N&#8221;IsUserTable&#8221;) = 0 or OBJECTPROPERTY(o.id, N&#8221;IsView&#8221;) = 1&#8217; Go<\/span><span style=\"color: #000000;\">Use<\/span><span style=\"color: #000000;\">AdventureWorks GO &#8211;Display the stored procedures&#8217; script text EXECUTE sp_MSforeachtable @command1 = &#8216;sp_helptext [?];&#8217;, @whereand = &#8216;and OBJECTPROPERTY(o.id, N&#8221;IsUserTable&#8221;) = 0 or OBJECTPROPERTY(o.id, N&#8221;IsProcedure&#8221;) = 1&#8217; Go<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #000000;\">Script #4 demonstrate the usage of sp_MSForEachDb stored procedure. The first script runs DBCC CHECKDB command against all the database to check the allocation, logical and physical structural integrity of all the objects inside a database. The second script first excludes the system databases from the consideration and takes a backup of all the user databases.<\/span><\/p>\n<table width=\"600\">\n<tbody>\n<tr>\n<td><span style=\"color: #000000;\"><strong>Script #4 : sp_MSForEachDb system stored procedure<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"color: #000000;\">&#8211;Checks the allocation, logical and physical structural &#8211;integrity of all the objects of all the databases EXEC sp_MSForEachdb @command1 = &#8216;DBCC CHECKDB([?])&#8217; GO<\/span><span style=\"color: #000000;\">&#8211;Does Backup of all the databases except system databases<\/span><span style=\"color: #000000;\">DECLARE @cmd1 nvarchar(2000) SET @cmd1 = &#8216;IF &#8221;?&#8221; NOT IN(&#8221;master&#8221;, &#8221;model&#8221;, &#8221;tempdb&#8221;, &#8221;msdb&#8221;)&#8217; + &#8216;BEGIN &#8216; + &#8216;Print &#8221;Backing up ? database&#8230;&#8221;;&#8217; + &#8216;BACKUP DATABASE [?] TO DISK=&#8221;&#8217; + &#8216;D:\\?_&#8217; + replace(convert(varchar,GETDATE(),120),&#8217;:&#8217;,&#8221;) + &#8216;.bak&#8221;&#8217; + &#8216;END&#8217; EXEC sp_MSForEachdb @command1 = @cmd1 GO<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #000000;\"><strong>\u00a0<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>\u00a0<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Thanks for reading this article,<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Next steps :<\/strong><\/span><\/p>\n<ol>\n<li><span style=\"color: #000000;\"><b><strong>Add this script to your database toolkit<\/strong><\/b><\/span><\/li>\n<li><span style=\"color: #000000;\"><strong>Share this with your colleagues because Sharing is Learning<\/strong><\/span><\/li>\n<li><span style=\"color: #000000;\"><strong>Comment below if you need any assistance<\/strong><\/span><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Scenario Proposal There are times when you need to loop through all the databases or database objects to perform some tasks. For example you want to run a DBCC command against all the databases or take backups of all the databases on the server or you want to rebuild all the indexes of all the [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[32,3],"tags":[59,8,60,13,12],"_links":{"self":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/167"}],"collection":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/comments?post=167"}],"version-history":[{"count":5,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/167\/revisions"}],"predecessor-version":[{"id":224,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/167\/revisions\/224"}],"wp:attachment":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/media?parent=167"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/categories?post=167"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/tags?post=167"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}