Jak wykonać zapytanie do bazy danych o puste tabele

28

Z powodu niektórych „programistów”, nad którymi pracowaliśmy, mieliśmy problemy z pustymi tabelami. Odkryliśmy, że podczas przesyłania do chmury skopiowano kilka tabel, ale dane w nich nie zostały.

Chciałbym uruchomić zapytanie w tabelach systemowych, aby dowiedzieć się, które tabele użytkowników są puste. Używamy MS SQL 2008 R2.

Dzięki za pomoc.

młot szyfrowy
źródło

Odpowiedzi:

46

Dźwignia finansowa sys.tablesi 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;

Użyj sumy wierszy, aby upewnić się, że nie pomylisz się z tabelami podzielonymi na partycje. Index_ID 0 lub 1 oznacza, że ​​patrzysz tylko na liczbę wierszy dla twoich stosów lub indeksów klastrowych.

Mike Fal
źródło
9

Jak zauważyli Mike Fal i Kin, tabele systemowe są twoim przyjacielem.

W przypadku wersji z pełnym kodem wymyśliłem następujące, które pozwolą ci zobaczyć całkowitą przestrzeń danych używaną przez każdą tabelę w bazie danych.

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;

Wyniki ostatnich 3 stwierdzeń:

wprowadź opis zdjęcia tutaj

Max Vernon
źródło
6

Oto wersja PowerShell:

Korzystanie z obiektów zarządzania 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
}

W zależności od liczby baz danych, możesz użyć powyższej funkcji na liście każdej nazwy bazy danych wypełnionej zmienną i wypisać ją jednocześnie, jeśli masz do czynienia z jednym serwerem:


$DBList = 'MyDatabase1','MyDatabase2'

foreach ($d in $DBList) {
Find-EmptyTables -server MyServer -database $d | 
  Select @{Label="Database";Expression={$d}}, Schema, Name, RowCount
}
Shawn Melton
źródło
4

Inne odpowiedzi tutaj są świetne, ale dla kompletności: SQL Server Management Studio> kliknij prawym przyciskiem myszy DB> Raporty> Standardowe raporty> Wykorzystanie dysku według tabeli

onupdatecascade
źródło
Zwróciłoby to jednak wszystkie tabele, nie tylko te, które są puste. Nie sądzę, że możesz zastosować filtry do tych raportów.
Shawn Melton,
To prawda. Pad Pad Pad
onupdatecascade
Ale to bardzo dobra odpowiedź. Już w odległości 2 kliknięć, łatwiej niż kiedykolwiek.
Marian
Listę można jednak uporządkować według liczby rekordów
Robert Mikes
1

Generalnie po prostu tworzę zapytanie, które tworzy zapytanie, które chcę, a następnie wykonuję je ręcznie, ale jeśli chcesz to wszystko za jednym razem ...

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;
jmoreno
źródło
1

Jako dodatkowa odpowiedź sp_MSforeachtableprzydatna jest tutaj nieudokumentowana procedura przechowywana w systemie .

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 ;

Obowiązują zwykłe ostrzeżenia o nieudokumentowanych funkcjach.

Możesz zajrzeć do kodu źródłowego procedury w trybie master, jeśli jesteś ciekawy lub chcesz mieć pewność, że nie ma żadnych nieprzyjemnych efektów ubocznych. Używa dynamicznego SQL do budowania kursora, co jest niekorzystne z punktu widzenia wydajności (kursor = wolny!), Więc używaj tej procedury tylko do jednorazowego zadania.

Ponadto sp_MSforeachtablenie jest dostępny w bazie danych Azure.

Greenstone Walker
źródło
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
Dinesh Ramaian
źródło
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
Pavan Kumar Gupta
źródło
Nie sądzę, aby poprawiło to istniejące odpowiedzi
James Anderson