回答:
レバレッジsys.tables
とsys.partitions
:
select
t.name table_name,
s.name schema_name,
sum(p.rows) total_rows
from
sys.tables t
join sys.schemas s on (t.schema_id = s.schema_id)
join sys.partitions p on (t.object_id = p.object_id)
where p.index_id in (0,1)
group by t.name,s.name
having sum(p.rows) = 0;
パーティションテーブルと混同しないように、行の合計を使用します。Index_IDが0または1の場合、ヒープまたはクラスター化インデックスの行カウントのみを見ていることを意味します。
Mike FalとKinの両方が指摘したように、システムテーブルはあなたの友人です。
より完全なコードを作成するために、データベース内の各テーブルで使用される合計データ領域を確認できる次のものを作成しました。
USE master;
CREATE DATABASE TestDB;
GO
USE tempdb;
ALTER DATABASE TestDB SET RECOVERY SIMPLE;
GO
USE TestDB;
CREATE TABLE Test1 (
Test1ID INT NOT NULL PRIMARY KEY IDENTITY(1,1)
, TestData nvarchar(255) CONSTRAINT DF_Test1_TestData DEFAULT (NEWID())
);
GO
TRUNCATE TABLE Test1;
SELECT s.name + '.' + t.name AS TableName,
sum(p.rows) AS TotalRows,
SUM(au.data_pages) AS DataPagesUsed
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.partitions p ON t.object_id = p.object_id
INNER JOIN sys.allocation_units au ON p.hobt_id = au.container_id
WHERE au.type = 1 or au.type = 3
AND t.is_ms_shipped = 0
GROUP BY s.name, t.name
ORDER BY SUM(au.data_pages) DESC;
INSERT INTO Test1 DEFAULT VALUES;
SELECT s.name + '.' + t.name AS TableName,
sum(p.rows) AS TotalRows,
SUM(au.data_pages) AS DataPagesUsed
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.partitions p ON t.object_id = p.object_id
INNER JOIN sys.allocation_units au ON p.hobt_id = au.container_id
WHERE au.type = 1 or au.type = 3
AND t.is_ms_shipped = 0
GROUP BY s.name, t.name
ORDER BY SUM(au.data_pages) DESC;
最後の3つのステートメントの結果:
PowerShellバージョンは次のとおりです。
SQL Server管理オブジェクト(SMO)の使用
function Find-EmptyTables ($server,$database)
{
# Load SMO assembly
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$s = New-Object 'Microsoft.SqlServer.Management.Smo.Server' $server
$db = $s.Databases.Item($database)
$db.Tables | Where-Object { $_.RowCount -eq 0 } | Select Schema, Name, RowCount
}
データベースの数に応じて、変数に入力された各データベース名のリストに対して上記の関数を使用し、1つのサーバーを扱う場合はすべて同時に出力できます。
$DBList = 'MyDatabase1','MyDatabase2'
foreach ($d in $DBList) {
Find-EmptyTables -server MyServer -database $d |
Select @{Label="Database";Expression={$d}}, Schema, Name, RowCount
}
ここでの他の応答は素晴らしいですが、完全を期すために:SQL Server Management Studio> DBを右クリック>レポート>標準レポート>テーブルごとのディスク使用量
私は通常、必要なクエリを作成するクエリを作成し、それを手動で実行しますが、すべてを一度に実行したい場合は...
declare @sql nvarchar(max) ;
set @sql = ';with cte as (' + (select
(
SELECT case when row_number()
over (order by table_schema, table_name) = 1 then ' '
else ' union ' end +
'select count(*) rws, ''[' +
t.TABLE_SCHEMA +'].[' + t.table_name +
']'' tbl from ' + '['+
t.TABLE_SCHEMA + '].[' + TABLE_NAME + ']' +
CHAR(10) AS [data()]
FROM INFORMATION_SCHEMA.TABLES t
FOR XML PATH ('')
)) + ') select * from cte where rws = 0;'
execute sp_executesql @sql;
追加の答えとして、文書化されていないシステムストアドプロシージャsp_MSforeachtable
がここで役立ちます。
CREATE TABLE #CountRows ( TableName nvarchar(260), NumRows int) ;
GO
EXEC sp_MSforeachtable 'insert into #CountRows select ''?'', count(*) from ?' ;
SELECT * FROM #CountRows WHERE NumRows = 0 ORDER BY TableName ;
DROP TABLE #CountRows ;
文書化されていない機能に関する通常の警告が適用されます。
好奇心が強い場合や、厄介な副作用がないことを確認したい場合は、masterでプロシージャのソースコードを確認できます。動的SQLを使用してカーソルを構築しますが、これはパフォーマンスに悪いため(cursor = slow!)、1回限りのタスクにのみこの手順を使用します。
また、sp_MSforeachtable
Azure Databaseでは使用できません。
DECLARE @toCheck INT;
DECLARE @countoftables INT;
DECLARE @Qry NVARCHAR(100);
DECLARE @name VARCHAR(100);
BEGIN
IF object_id('TEMPDB.DBO.#temp') IS NOT NULL drop table #temp;
SELECT ROW_NUMBER() OVER(ORDER BY name) AS ROW,CountStatement = 'SELECT @toCheck = COUNT(*) FROM ' + name,name INTO #temp FROM SYS.TABLES WITH (NOLOCK)
--SELECT * FROM #temp ORDER BY ROW
SET @countoftables =(SELECT COUNT(*) FROM #temp)
WHILE (@countoftables > 0)
BEGIN
SET @Qry = (SELECT CountStatement FROM #temp WITH (NOLOCK) WHERE ROW = @countoftables);
SET @name = (SELECT name FROM #temp WITH (NOLOCK) WHERE ROW = @countoftables);
EXEC SP_EXECUTESQL @qry,N'@toCheck INT OUTPUT',@toCheck OUTPUT;
IF(@toCheck=0)
BEGIN
PRINT 'Table: ' + @name + ', count: ' + convert(varchar(10),@toCheck);
END
--ELSE
-- BEGIN
-- PRINT 'Table: ' + @name + ', count: ' + convert(varchar(10),@toCheck);
-- END
SET @countoftables = @countoftables -1;
END
END
SELECT T.name [Table Name],i.Rows [Number Of Rows]
FROM sys.tables T
JOIN sys.sysindexes I ON T.OBJECT_ID = I.ID
WHERE indid IN (0,1) AND i.Rows<1
ORDER BY i.Rows DESC,T.name