【问题标题】:T-SQL query each database in instance without using a cursorT-SQL 在不使用游标的情况下查询实例中的每个数据库
【发布时间】:2016-01-05 22:21:12
【问题描述】:

使用 SQL Server 2012。

使用为每个数据库存储的系统视图,我想保留整个实例中所有索引的索引行为摘要。下面的代码收集了一些统计数据并将它们放在一个表中以供以后使用。问题是,我不得不使用游标来遍历实例中的数据库。任何人都可以提出一个更好的方法来做到这一点而不必使用光标?

USE Master
GO

-- If the procedure already exists then update it. 
IF OBJECT_ID('sp_index_stats') IS NOT NULL 
    DROP PROCEDURE sp_index_stats
GO

CREATE PROCEDURE sp_index_stats AS
    DECLARE     @dbname     NVARCHAR(128) 
    DECLARE     @finalSQL   NVARCHAR(1024)

    -- Results of the query are put into a table in Demo_DB. 
    -- So empty it now for the new results. 

    TRUNCATE TABLE Demo_DB.dbo.index_stats

    -- Use a cursor to get a list of all the tables in the instance. 
    DECLARE database_cursor CURSOR LOCAL FOR
        SELECT  name
        FROM    Master.dbo.sysdatabases
        WHERE   name NOT IN ('master', 'model', 'msdb', 'tempdb')

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @dbname

    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Each database will contain the same structue, so I need
        -- to create a standard query just changing the database name.
        SET @finalSQL = 'INSERT INTO Demo_DB.dbo.index_stats '

        -- Sort out the select statement.
        SET @finalSQL = @finalSQL + 'SELECT sdb.name, st.name, si.name, si.type_desc, '
        SET @finalSQL = @finalSQL + 'si.is_primary_key, si.fill_factor, '
        SET @finalSQL = @finalSQL + 'sd.user_scans, sd.last_user_scan, '
        SET @finalSQL = @finalSQL + 'sd.user_updates, sd.last_user_update '

        -- The 'from' and 'joins' need to be adjusted slightly for each databse. 
        SET @finalSQL = @finalSQL + 'FROM ' + @dbname + '.sys.tables st '
        SET @finalSQL = @finalSQL + 'JOIN ' + @dbname + '.sys.indexes si ON (si.object_id = st.object_id) '
        SET @finalSQL = @finalSQL + 'JOIN ' + @dbname + '.sys.dm_db_index_usage_stats sd ON (si.object_id = sd.object_id) '
        SET @finalSQL = @finalSQL + 'JOIN ' + @dbname + '.sys.databases sdb ON (sdb.database_id = sd.database_id) ;'

        -- Ok, let's run that...
        EXEC (@finalSQL) 

        -- Get the next database in the instance..,
        FETCH NEXT FROM database_cursor INTO @dbname
    END
    CLOSE database_cursor
    DEALLOCATE database_cursor
GO

问候,

JC.

【问题讨论】:

  • 这是一个名为 sp_MSforeachdb 的未记录过程。然而,似乎其他 T-SQL 程序员认为不应该使用它:stackoverflow.com/questions/20131930/… 游标的替代方法是使用 While,如下所述:stackoverflow.com/questions/61967/…
  • 光标有什么问题?你认为另一种方法会更快吗?为什么?此过程在运行时的主要成本将是从每个数据库中单独提取所有统计信息/元数据,而不是围绕生成这些单独查询的脚手架。
  • @AaronBertrand:我不认为循环比游标更好。我也不相信相反的情况。但据我所知,网络上关于这个话题的讨论有些争议:一些支持游标,一些支持循环with table variables and PK,还有一些说这取决于具体情况(服务器版本、设置、音量等)。 James 要求提供一个光标的替代方案,我让他知道了一个替代方案(同时没有评估它)。我希望没关系。
  • @Ralph 这只是一个常见的误解,即 while 循环比游标快。是的,它是一种替代品,就像红色法拉利是黄色法拉利的替代品一样。

标签: sql-server tsql


【解决方案1】:

串联呢?

类似这样的东西(没有复制整个存储过程,但希望你能明白要点)

DECLARE @SQL VARCHAR(MAX)

SET @SQL = ''

SELECT @SQL = @SQL + 
'INSERT INTO Demo_DB.dbo.index_stats ' +
'SELECT sdb.name, st.name, si.name, si.type_desc, ' +
'si.is_primary_key, si.fill_factor, ' +
'sd.user_scans, sd.last_user_scan, ' +
'sd.user_updates, sd.last_user_update ' +
'FROM ' + name + '.sys.tables st ' +
'JOIN ' + name + '.sys.indexes si ON (si.object_id = st.object_id) ' +
'JOIN ' + name + '.sys.dm_db_index_usage_stats sd ON (si.object_id = sd.object_id) ' +
'JOIN ' + name + '.sys.databases sdb ON (sdb.database_id = sd.database_id) ;'  
FROM    Master.dbo.sysdatabases
WHERE   name NOT IN ('master', 'model', 'msdb', 'tempdb')

SELECT @SQL

(显然此时没有执行任何操作)。

【讨论】:

  • 谢谢 Fruitbat,就是这样。除了关于游标和循环的争论之外,当我写出代码时,我确信有一个更好的 SQL 解决方案,但看不到它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-05
  • 1970-01-01
相关资源
最近更新 更多