2

サーバー内のすべてのテーブルの行数を取得しようとしています (特定のデータベースではなく、msdb、モデル、マスターなどを除く、サーバー上のすべてのデータベース)。データベース名、テーブル名、および行数以外の詳細を返す必要はありません。

この問題に対する私のアプローチは、サーバー内のすべてのデータベースを取得し、それらに ID を配置することです。これは while ループで参照されます (ID 1 から始まり、最大 ID まで)。次に、while ループ内で、一致するデータベース ID のテーブルと行数を取得します。私の問題は、USE DatabaseName では動的にすることができないように見えることです。つまり、データベース名を変数に格納して、行カウント クエリを使用してテーブルを実行するときに参照データベースとして使用することはできません。

私が見逃している別のアプローチはありますか(他の多くの例を見てきました-カーソルを使用することが多く、コードがはるかに長く、より多くのリソースを使用しているように見えます.次のデータベースなどにヒットしないことを除いて、テーブルごとの最大のデータベース)、またはこれを動的にするコードに明らかな何かが欠けていますか?

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'
4

2 に答える 2

3

いいえ、それは基本的にあなたのやり方です。

while ループがカーソルよりも速いと考える理由がわかりません(ただし、これは一般的な誤解です) 。それらは本質的に同じものです。私は常にカーソルを使用しているわけではありませんが、使用する場合は使用しますLOCAL FAST_FORWARD- あなたも必ず使用してください。詳細については、次の記事を参照してください。

このような個々のタスクに必要なコードを減らすために、sp_MSforeachdb私が書いた置換に興味があるかもしれません (sp_MSforeachdbは、データベースごとにコマンドを繰り返す組み込みの、文書化されておらず、サポートされていないストアド プロシージャですが、たとえば、システムデータベースを除外し、実行を停止することがある重大なバグもあります):

もう 1 つの方法は、動的 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;

ここで、これがカーソルやループも使用していないと信じ込まないでください。実際に使用されています。ただし、コマンドをループで実行するのではなく、ループでコマンドを作成しています。

于 2013-03-27T20:25:47.217 に答える
0

using を使用するのではなく、動的クエリを作成するという点では、選択した@db変数を使用して、テーブル名の完全修飾名を作成できます。

'FROM ' + @db+'.sys.objects'などとなりますので。

DB 名が有効であることを確認する必要があります (たとえば、何らかの理由で括弧が必要な名前がある場合)。

于 2013-03-27T20:27:56.163 に答える