TSQL:服务器中所有表的行计数

时间:2013-03-27 20:18:27

标签: tsql sql-server-2008-r2

我试图获取服务器中所有表的行数(不是特定数据库,而是服务器上的所有数据库,不包括msdb,model,master等)。除了数据库名称,表名和行数之外,我不需要返回任何其他详细信息。

我解决这个问题的方法是获取服务器中的所有数据库并在其上放置一个id,这将在while循环中引用(从id 1开始直到最大id)。然后,在while循环中,我获取匹配的数据库ID中的表和行计数。我的问题是USE DatabaseName似乎不允许我将其设置为动态,这意味着我无法在变量中存储数据库名称,并在执行带有行计数查询的表时将其用作引用数据库。 / p>

我是否还缺少另一种方法(我已经看过很多其他的例子 - 经常使用游标,代码似乎要长得多,并且似乎使用了更多的资源 - 这甚至是一个相对快速的查询如果我按表使用最大的数据库,除了它没有访问下一个数据库等等,或者我是否遗漏了代码中显而易见的东西以使其动态化?

DECLARE @ServerTable TABLE(
    DatabaseID INT IDENTITY(1,1),
    DatabaseName VARCHAR(50)
)

DECLARE @count INT
DECLARE @start INT = 1
SELECT @count = COUNT(*) FROM sys.databases WHERE name NOT IN ('master','tempdb','model','msdb')

INSERT INTO @ServerTable (DatabaseName)
SELECT name 
FROM sys.databases
WHERE name NOT IN ('master','tempdb','model','msdb')

WHILE @start < @count
BEGIN

    DECLARE @db VARCHAR(50)
    SELECT @db = DatabaseName FROM @ServerTable WHERE DatabaseID = @start

    -- This is the problem, as the USE doesn't seem to allow it to be dynamic.
    USE @db
    GO

    SELECT @db
        ,o.name [Name]
        ,ddps.row_count [Row Count]
    FROM sys.indexes AS i
        INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
        INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID AND i.index_id = ddps.index_id 
    WHERE i.index_id < 2  AND o.is_ms_shipped = 0 
    ORDER BY o.NAME

    SET @start = @start + 1
END

注意:我尝试检查sys.objects和sys.indexes以查看是否可以使用数据库名称进行过滤,但我没有运气。

更新:我尝试将SELECT变为动态而没有成功(请注意以下代码仅显示更改SELECT):

SET @sql = '
    SELECT ' + @db + ' [Database]
        ,o.name [Name]
        ,ddps.row_count [Row Count]
    FROM  ' + @db + '.sys.objects
        INNER JOIN ' + @db + ' sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
        INNER JOIN ' + @db + ' sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID AND i.index_id = ddps.index_id 
    WHERE i.index_id < 2  AND o.is_ms_shipped = 0 
    ORDER BY o.NAME'

2 个答案:

答案 0 :(得分:2)

不,这基本上就是你这样做的方式。

我不确定你为何选择a while loop is faster than a cursor (though this is a common misconception)。它们基本上是一回事。我并不总是使用游标,但是当我这样做时,我会使用LOCAL FAST_FORWARD - 确保你也这样做。有关详细信息,请参阅此文章:

http://www.sqlperformance.com/2012/09/t-sql-queries/cursor-options

要减少像这样的单个任务所需的代码,您可能会对我写的sp_MSforeachdb替换感兴趣(sp_MSforeachdb是一个内置的,未记录且不受支持的存储过程,它将重复命令对于每个数据库,但是不可能,例如,过滤掉系统数据库,它也有一个严重的错误,它有时会停止执行):

另一种方式是动态SQL。

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += '
  SELECT db = N''' + name + '''
    ,o.name [Name]
    ,ddps.row_count [Row Count]
  FROM ' + QUOTENAME(name) + '.sys.indexes AS i
    INNER JOIN ' + QUOTENAME(name) + '.sys.objects AS o 
      ON i.OBJECT_ID = o.OBJECT_ID
    INNER JOIN ' + QUOTENAME(name) + '.sys.dm_db_partition_stats AS ddps 
      ON i.OBJECT_ID = ddps.OBJECT_ID AND i.index_id = ddps.index_id 
    WHERE i.index_id < 2  AND o.is_ms_shipped = 0 
    ORDER BY o.NAME;'
FROM sys.databases 
WHERE database_id > 4;

PRINT @sql;
--EXEC sp_executesql @sql;

(打印在那里,以便您可以在执行之前检查命令。如果您有大量数据库,它可能会被截断为8K,但不要惊慌 - 这只是SSMS中的显示问题,命令完成。)

您还可以首先构建一个#temp表,并插入其中,以便您可以使用单个结果集,例如

CREATE TABLE #x(db SYSNAME, o SYSNAME, rc SYSNAME);

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += 'INSERT #x(db,o,rc)
  SELECT db = N''' + name + '''
    ,o.name [Name]
    ,ddps.row_count [Row Count]
  FROM ' + QUOTENAME(name) + '.sys.indexes AS i
    INNER JOIN ' + QUOTENAME(name) + '.sys.objects AS o 
      ON i.OBJECT_ID = o.OBJECT_ID
    INNER JOIN ' + QUOTENAME(name) + '.sys.dm_db_partition_stats AS ddps 
      ON i.OBJECT_ID = ddps.OBJECT_ID AND i.index_id = ddps.index_id 
    WHERE i.index_id < 2  AND o.is_ms_shipped = 0 
    ORDER BY o.NAME;'
FROM sys.databases 
WHERE database_id > 4;

EXEC sp_executesql @sql;

SELECT db, o, rc FROM #x ORDER BY db, o;

现在,不要被愚弄相信这也不是使用游标或循环 - 它是。但是它在循环中构建命令而不是在循环中执行它。

答案 1 :(得分:0)

就制作动态查询而言,您可以使用所选的@db变量为表格名称执行完全限定名称,而不是使用使用。

所以它会是'FROM ' + @db+'.sys.objects'等。

您必须检查您的数据库名称是否有效(例如,如果由于某种原因您的名称需要括号)。