En raison de certains "développeurs" que nous avons dû travailler sur notre système, nous avons eu des problèmes avec des tables vides. Nous avons constaté que lors du transfert vers le cloud, plusieurs tables ont été copiées, mais pas les données qu'elles contiennent.
Je voudrais lancer une requête sur les tables système pour trouver quelles tables utilisateur sont vides. Nous utilisons MS SQL 2008 R2.
Merci pour l'aide.
Effet de levier sys.tables
et 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;
Utilisez une somme de lignes pour vous assurer de ne pas avoir de confusion avec les tables partitionnées. Index_ID de 0 ou 1 signifie que vous ne regardez que le nombre de lignes pour vos segments ou index cluster.
Comme l'ont noté Mike Fal et Kin, les tables système sont votre ami.
Pour une version plus complète du code, j'ai trouvé ce qui suit, qui vous permettrait de voir l'espace total de données utilisé par chaque table de votre base de données.
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;
Résultats des 3 dernières déclarations:
Voici une version PowerShell:
Utilisation des objets de gestion 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
}
En fonction du nombre de bases de données, vous pouvez utiliser la fonction ci-dessus par rapport à une liste de chaque nom de base de données remplie dans une variable et les afficher toutes en même temps, si vous traitez avec un serveur:
$DBList = 'MyDatabase1','MyDatabase2'
foreach ($d in $DBList) {
Find-EmptyTables -server MyServer -database $d |
Select @{Label="Database";Expression={$d}}, Schema, Name, RowCount
}
Les autres réponses ici sont excellentes, mais pour être complet: SQL Server Management Studio> cliquez avec le bouton droit sur la base de données> Rapports> Rapports standard> Utilisation du disque par table
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
Je crée généralement une requête qui crée la requête que je veux, puis je l'exécute manuellement, mais si vous le souhaitez en une seule fois ...
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;
Comme réponse supplémentaire, la procédure stockée système non documentée sp_MSforeachtable
est utile ici.
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 ;
Les avertissements habituels concernant les fonctionnalités non documentées s'appliquent.
Vous pouvez consulter le code source de la procédure dans master si vous êtes curieux ou si vous voulez être certain qu'il n'a pas d'effets secondaires désagréables. Il utilise du SQL dynamique pour construire un curseur, ce qui est mauvais pour les performances (curseur = lent!), Donc n'utilisez cette procédure que pour une tâche ponctuelle.
Aditionellement, sp_MSforeachtable
n'est pas disponible dans la base de données Azure.