Feedback

VB - Größe einer SQL-Server Datenbank ermitteln

Veröffentlicht von am 17.07.2009
(2 Bewertungen)
Bei Datenbankanwendungen sollte man stets die Datenbankgröße im Auge haben. Das gilt besonders für Nutzer der SQL-Server Express Versionen, da diese auf 4GB beschränkt sind. Mit diesem Beispiel möchte ich zeigen, wie die Datenbankgröße einer SQL-Server Datenbank ermittelt werden kann.

Viel Spass bei der kreativen Weiterverwendung.

'''''' <summary>
    '''''' Gibt die Größe einer SQL-Server Datenbank zurück
    '''''' </summary>
    '''''' <param name="database">Name der Datenbank</param>
    '''''' <returns>size + MB</returns>
    '''''' <remarks></remarks>
    Function GetDatabaseSize(ByVal database As String) As String

        Using conn As New SqlConnection("connectionString")

            Dim sql As String = "USE " & database & "; exec sp_spaceused @updateusage=true"
            Dim cmd As New SqlCommand(sql, conn)
            Dim dA As New SqlDataAdapter(cmd)
            Dim sizeDataSet As New DataSet()

            Try
                conn.Open()
                dA.Fill(sizeDataSet, "SizeInfoTable")

                Return sizeDataSet.Tables("SizeInfoTable").Rows(0)("database_size").ToString()
                ''''Die Tabelle stellt noch weitere Spalten bereit.
                ''''Infos unter: http://msdn.microsoft.com/de-de/library/ms188776.aspx
            Catch
                Return "nicht erfasst"
            End Try

        End Using

    End Function

1 Kommentare zum Snippet

AI schrieb am 22.01.2026:
Das Snippet ist von 2009 und trifft den damaligen Pragmatismus: sp_spaceused aufrufen und irgendeinen String zurückgeben. In aktuellen .NET- und SQL-Server-Setups hat der Ansatz aber ein paar echte Baustellen: SQL-Injection durch String-Konkatenation, unnötig schwere ADO.NET-Objekte (DataSet), potenziell teure @updateusage=true-Statistikanpassung und „Catch-all und schlucke alles“.

Analyse nach heutigen Kriterien:
- Sicherheit: "USE " & database wird direkt in SQL zusammengebaut. Ein manipuliertes database-Argument kann zu SQL-Injection führen. "USE" lässt sich nicht parametrieren, daher muss die Datenbankauswahl anders gelöst werden (ConnectionString/ChangeDatabase oder Whitelist).
- Korrektheit: sp_spaceused liefert Strings wie "123.45 MB" und ist regional/formatabhängig. Das ist für Weiterverarbeitung ungeeignet. Außerdem liefert der Code nur die erste Zeile/Spalte und ignoriert, dass sp_spaceused mehrere Ergebniswerte liefert.
- Performance: @updateusage=true kann sehr teuer sein, weil es Nutzungsstatistiken aktualisiert. Für „Monitoring“ ist das in der Regel falsch; man will die Größe schnell abfragen, nicht Wartungsarbeit triggern.
- Memory-Allokationen: DataSet/SqlDataAdapter sind für eine einzelne Kennzahl Overkill und verursachen unnötige Allokationen.
- Robustheit: Catch ohne Exception-Info und Rückgabe "nicht erfasst" macht Fehlerdiagnose praktisch unmöglich.
- Cloud/Container: In Azure SQL Database ist das Konzept „USE andere Datenbank“ typischerweise nicht gewünscht; pro Verbindung eine DB ist der Normalfall. Streamlined SQL und Microsoft.Data.SqlClient sind heute die realistischere Basis.
- Thread-Safety: Die Methode ist an sich thread-safe, solange jede Ausführung ihre eigene Connection verwendet. Die eigentliche Gefahr liegt in konkurrierenden Monitoring-Calls mit @updateusage=true.

Modernisierte Variante (ohne USE, ohne DataSet, numerische Werte, optional Data/Log getrennt):

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;

public static class SqlDatabaseSize
{
public static async Task<(long DataBytes, long LogBytes)> GetDatabaseFileSizeBytesAsync(string connectionString, CancellationToken cancellationToken = default)
{
if (connectionString is null) throw new ArgumentNullException(nameof(connectionString));

const string sql = @"
SELECT
SUM(CASE WHEN type_desc = 'ROWS' THEN size END) * 8192 AS DataBytes,
SUM(CASE WHEN type_desc = 'LOG' THEN size END) * 8192 AS LogBytes
FROM sys.database_files;";

await using var conn = new SqlConnection(connectionString);
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);

await using var cmd = new SqlCommand(sql, conn);
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);

if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) return (0, 0);

long dataBytes = reader.IsDBNull(0) ? 0 : reader.GetInt64(0);
long logBytes = reader.IsDBNull(1) ? 0 : reader.GetInt64(1);

return (dataBytes, logBytes);
}

public static async Task<long> GetDatabaseSizeBytesAsync(string connectionString, CancellationToken cancellationToken = default)
{
var (dataBytes, logBytes) = await GetDatabaseFileSizeBytesAsync(connectionString, cancellationToken).ConfigureAwait(false);
return checked(dataBytes + logBytes);
}
}


Warum das heute objektiv besser ist:
- Sicherheit: Kein dynamisches "USE <db>" aus User-Input. Die DB-Auswahl erfolgt sauber über den ConnectionString (Initial Catalog) oder über eine kontrollierte Quelle.
- Performance: Keine Nebenwirkungen wie @updateusage=true. Die Abfrage ist eine reine Metadatenabfrage auf sys.database_files.
- Memory-Allokationen: Kein DataSet/Adapter, nur Reader und primitive Werte.
- Robustheit: Fehler werden nicht verschluckt. Der Aufrufer bekommt saubere Exceptions und kann Logging/Retry sinnvoll implementieren.
- Cloud/Container: Passt zu dem Modell „eine Verbindung = eine Datenbank“ (z. B. Azure SQL Database) und funktioniert gut in Services/Containern.

Security-Realitätscheck:
Datenbanknamen aus User-Eingaben dürfen nie ungeprüft in SQL landen. Wenn du zwingend eine DB per Name auswählen musst, dann nur über eine Whitelist oder indem du vorab die DB-Namen serverseitig ermittelst und gegen bekannte Werte validierst, statt String-Konkatenation.
 

Logge dich ein, um hier zu kommentieren!