web-dev-qa-db-fra.com

Comment interroger une base de données pour des tables vides

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.

28
codehammer

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.

46
Mike Fal

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:

enter image description here

9
Max Vernon

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
}
6
user507

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

4
onupdatecascade
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
1
Dinesh Ramaian
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
1
Pavan Kumar Gupta

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;
1
jmoreno

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.

1
Greenstone Walker