Problem with exec msforeachdb

While attempting to come up with a way to gather index information and store it in my newly created warehouse, I am using the sys.dm_db_index_physical_stats dmv.  At first, I was had plan to call this DMV via linked server such as: [serverA].master.sys.dm_db_index_physical_stats except that SQL won’t let me do that. 

Other DMV’s I’ve used recently allowed me to make calls via linked server.  Live and learn I suppose.  Anyways, msforeachdb is an undocumented procedure that loop through each database and do what you tell it to do, such as changing all the databases from FULL to SIMPLE mode.  It work pretty flawlessly via ad-hoc but the moment you throw this sucker into a stored procedure and execute it using the bcp command, it crap out.  In fact it works fine if you call it within a sql job or through a SSMS query window.  The error that bcp gave me is:

The syntax is pretty much common and found on almost all blogs that discuss SQL:

exec sp_msforeachdb 'select
@@SERVERNAME
, DB_NAME(s.database_id) as name
, OBJECT_NAME(s.object_id) as tableName
, i.name
, s.index_type_desc as indexType
, s.index_depth as indexDepth
, s.page_count as pageCount
, convert(float,round(s.avg_fragmentation_in_percent,2)) as avgFragInPercent
, convert(float,round(s.avg_fragment_size_in_pages,2)) as avgFragSizeInPages
, getdate()
from sys.dm_db_index_physical_stats(db_id(),null, null, null, ''limited'') as s
join sys.indexes i ON s.[object_id] = i.[object_id]
AND s.index_id = i.index_id
INNER JOIN sys.objects o ON i.object_id = O.object_id
where s.index_id = 1
AND s.avg_fragmentation_in_percent >= 50
AND s.database_id = DB_ID()
AND i.name IS NOT NULL
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0'

At first, I was confused as to why it is attempting to use #qtemp.  Nowhere in my script said to use any temp table.  So I asked SQL to give me the execution plan and opened it up with SQL Sentry.  You can see that msforeachdb make use of cursors and created 10 #qtemp objects.  The note stated /* look for each database */ except that I have 9 databases and not 10.

In the SSMS’ and SQL Sentry execution plan display, it executed 111 times!  It is a pretty mess plan.  So I had to rewrite the query using a while loop and stick it inside the procedure.  Using the while loop executed the query only 28 times and complete much much faster.

As of right now, I haven’t found the reason for the original failed message when attempting to use msforeachdb inside a store procedure.  But I have a feeling that it might have to do the #qtemp table being accessible via the stored procedure and that it is a local temp table not global.

  • Daniel Ring

    Hey there, I’m having a similar issue…

    BEGIN
    INSERT SQLAdmin.dbo.healthcheck_indexfrag
    EXEC (‘SELECT * FROM OPENQUERY([' + @SERVER + '] ,”
    EXEC sp_msforeachdb ””USE [?];
    SELECT @@SERVERNAME,DB_NAME(ps.database_id),OBJECT_NAME(ps.object_id),b.name,ps.avg_fragmentation_in_percent,ps.fragment_count,
    ps.index_type_desc,ps.page_count,ps.database_id,ps.index_id,getdate()
    FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS ps
    INNER JOIN sys.indexes AS b ON ps.OBJECT_ID = b.OBJECT_ID AND ps.index_id = b.index_id
    WHERE ps.database_id = DB_ID()
    AND ps.avg_fragmentation_in_percent > 20
    AND ps.fragment_count > 50
    ORDER BY ps.fragment_count desc, ps.avg_fragmentation_in_percent””;”)’)
    END

    SELECT * from SQLAdmin.dbo.healthcheck_indexfrag

    I get the error message -

    Msg 208, Level 16, State 1, Procedure sp_MSforeach_worker, Line 102
    Invalid object name ‘#qtemp’.

    I’m planning on using this or a similar query to get index frag statistics from many servers at once, so altering a system stored procedure on EVERY instance is not something I can do…

    Any ideas on how I can get around this?

    • red8rain

      I have been thinking about this a bit more and haven’t tried it out myself. But you should be able to put that statement into a SSIS package, have it execute on said server, store the output in a table and than export it elsewhere for storage.

  • red8rain

    Hi Daniel. Apologize for the delayed response. I don’t normally get comments to any of my postings … As stated in my post, I wasn’t able to get past the error “Invalid object name ‘#qtemp’.” However, I just came across a CMS for SQL server last week, haven’t used it exactly but it seem promising. You can check it out at http://sqlcms.codeplex.com/. That is what I’m thinking about using. Hope that was helpful.