Come eseguire una query su un database per tabelle vuote


28

A causa di alcuni "sviluppatori" che stavano lavorando sul nostro sistema, abbiamo riscontrato problemi con tabelle vuote. Abbiamo scoperto che durante il trasferimento sul cloud sono state copiate diverse tabelle, ma i dati in esse non lo erano.

Vorrei eseguire una query nelle tabelle di sistema per trovare quali tabelle utente sono vuote. Stiamo utilizzando MS SQL 2008 R2.

Grazie per l'aiuto.

Risposte:


46

Leva sys.tablese 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;

Usa una somma di righe solo per assicurarti di non avere confusione con le tabelle partizionate. Index_ID di 0 o 1 significa che stai solo guardando i conteggi delle righe per i tuoi heap o indici cluster.


9

Come hanno notato Mike Fal e Kin, le tabelle di sistema sono tue amiche.

Per una versione più completa del codice, ho escogitato quanto segue, che ti permetterebbe di vedere lo spazio dati totale utilizzato da ogni tabella nel tuo database.

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;

Risultati delle ultime 3 affermazioni:

inserisci qui la descrizione dell'immagine


6

Ecco una versione di PowerShell:

Utilizzo di SQL Server Management Objects (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
}

A seconda del numero di database, è possibile utilizzare la funzione di cui sopra in un elenco di ciascun nome di database popolato in una variabile e restituirlo contemporaneamente, se si tratta di un server:


$DBList = 'MyDatabase1','MyDatabase2'

foreach ($d in $DBList) {
Find-EmptyTables -server MyServer -database $d | 
  Select @{Label="Database";Expression={$d}}, Schema, Name, RowCount
}

4

Altre risposte qui sono ottime, ma per completezza: SQL Server Management Studio> fare clic con il tasto destro del mouse su DB> Rapporti> Rapporti standard> Utilizzo del disco per tabella


Ciò restituirebbe tuttavia tutte le tabelle, non solo quelle vuote. Non credo che tu possa applicare filtri a quei rapporti.
Shawn Melton,

Questo è vero. Pad Pad Pad
onupdatecascade

Ma è un'ottima risposta. Solo a 2 clic di distanza, più facile che mai.
Marian,

Tuttavia, è possibile ordinare l'elenco in base al numero di record
Robert Mikes,

1

Generalmente creo solo una query che crea la query che desidero e quindi eseguirla manualmente, ma se vuoi tutto in una volta ...

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

Come risposta aggiuntiva, la procedura memorizzata di sistema non documentata sp_MSforeachtableè utile qui.

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 ;

Si applicano le consuete avvertenze sulle funzionalità non documentate.

Puoi guardare il codice sorgente della procedura in master se sei curioso o se vuoi essere certo che non abbia effetti collaterali cattivi. Utilizza SQL dinamico per creare un cursore, il che è negativo per le prestazioni (cursore = lento!), Quindi usa questa procedura solo per un'attività una tantum.

Inoltre, sp_MSforeachtablenon è disponibile nel database di Azure.


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

Non penso che questo migliora le risposte esistenti
James Anderson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.