Sprache: VB
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
'''''' <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
Alte URL:
/snippet/groesse-einer-sql-server-datenbank-ermitteln/1185
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“.
[b]Analyse nach heutigen Kriterien:[/b]
– [u]Sicherheit[/u]: „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).
– [u]Korrektheit[/u]: 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.
– [u]Performance[/u]: @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.
– [u]Memory-Allokationen[/u]: DataSet/SqlDataAdapter sind für eine einzelne Kennzahl Overkill und verursachen unnötige Allokationen.
– [u]Robustheit[/u]: Catch ohne Exception-Info und Rückgabe „nicht erfasst“ macht Fehlerdiagnose praktisch unmöglich.
– [u]Cloud/Container[/u]: 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.
– [u]Thread-Safety[/u]: 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.
[b]Modernisierte Variante (ohne USE, ohne DataSet, numerische Werte, optional Data/Log getrennt):[/b]
[code]
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 GetDatabaseSizeBytesAsync(string connectionString, CancellationToken cancellationToken = default)
{
var (dataBytes, logBytes) = await GetDatabaseFileSizeBytesAsync(connectionString, cancellationToken).ConfigureAwait(false);
return checked(dataBytes + logBytes);
}
}
[/code]
[b]Warum das heute objektiv besser ist:[/b]“ aus User-Input. Die DB-Auswahl erfolgt sauber über den ConnectionString (Initial Catalog) oder über eine kontrollierte Quelle.
– [u]Sicherheit[/u]: Kein dynamisches „USE
– [u]Performance[/u]: Keine Nebenwirkungen wie @updateusage=true. Die Abfrage ist eine reine Metadatenabfrage auf sys.database_files.
– [u]Memory-Allokationen[/u]: Kein DataSet/Adapter, nur Reader und primitive Werte.
– [u]Robustheit[/u]: Fehler werden nicht verschluckt. Der Aufrufer bekommt saubere Exceptions und kann Logging/Retry sinnvoll implementieren.
– [u]Cloud/Container[/u]: Passt zu dem Modell „eine Verbindung = eine Datenbank“ (z. B. Azure SQL Database) und funktioniert gut in Services/Containern.
[b]Security-Realitätscheck:[/b]
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.