it-swarm.com.de

Was verursacht einen arithmetischen Überlauf in der folgenden Abfrage?

wenn ich die folgende Abfrage in MY_Database ausführe

select * from sys.sysfiles

Ich erhalte folgende Ergebnisse:

(enter image description here

Wenn ich jedoch eine dynamische Abfrage ausführe, die den Prozentsatz des freien Speicherplatzes erhält, den ich erhalte:

Meldung 8115, Ebene 16, Status 7, Zeile 93 Arithmetischer Überlauffehler beim Konvertieren von numerischen in numerische Datentypen.

DECLARE @command NVARCHAR(MAX) 
SELECT @command = 'SELECT  db_name() as db_name,
CAST(S.size/128.0 - CAST(FILEPROPERTY(S.name, ' + '''' +  
               'SpaceUsed' + '''' + ' ) AS int)/128.0 AS int) AS FreeSpaceMB  
        ,CAST(100 * (CAST (((S.size/128.0 -CAST(FILEPROPERTY(S.name,  
        ' + '''' + 'SpaceUsed' + '''' + ' ) AS int)/128.0)/(S.size/128.0))  
        AS decimal(5,2))) AS varchar(8)) + ' + '''' +  '''' + ' AS FreeSpacePct
        FROM sys.sysfiles S'  

exec sp_executesql @statement = @command

Ich habe Mühe, den Grund für den arithmetischen Überlauf zu finden. warum passiert es

Warum durch 128 teilen? Dies liegt daran, dass sowohl sys.sysfiles als auch FILEPROPERTY die Anzahl der 8K-Seiten und nicht MB angeben und von 8K-Seiten in konvertieren MB dividieren Sie durch 128 wie hier erklärt

Warum ist es dynamisch?

Weil ich tatsächlich die Werte aus jeder Datenbank mit sp_ForEachDB bekomme, wie Sie im folgenden Beispiel sehen können:

DECLARE @command VARCHAR(5000) 
SELECT @command = 'Use ' + '?' + ' SELECT  db_name() as db_name,
CAST(S.size/128.0 - CAST(FILEPROPERTY(S.name, ' + '''' +  
               'SpaceUsed' + '''' + ' ) AS int)/128.0 AS int) AS FreeSpaceMB  
        --,CAST(100 * (CAST (((S.size/128.0 -CAST(FILEPROPERTY(S.name,  
        --' + '''' + 'SpaceUsed' + '''' + ' ) AS int)/128.0)/(S.size/128.0))  
        --AS decimal(4,2))) AS varchar(8)) + ' + '''' +  '''' + ' AS FreeSpacePct
        FROM dbo.sysfiles S'  

EXEC sp_ForEachDB @command  
5

Ich habe Mühe, den Grund für den arithmetischen Überlauf zu finden. warum passiert es

Höchstwahrscheinlich geben die Metadaten einige unerwartete Werte zurück, die Ihr Code nicht verarbeiten kann. Zum Beispiel:

-- Example values returned from sysfiles and FILEPROPERTY
DECLARE 
    @size integer = 1,
    @spaceused integer = 10000;

-- The essence of the code in the question
SELECT
    CAST
    (
        100 * 
        (
            CAST 
            (
                (
                    (@size/128.0 - @spaceused/128.0)/(@size/128.0)
                )  
                AS decimal(5,2)
            )
        ) 
        AS varchar(8)
    ) + '' AS FreeSpacePct;

... gibt den in der Frage genannten Fehler zurück, da der berechnete (negative!) Wert nicht in decimal(5,2) passt.

Es gibt Gründe, warum die Größe möglicherweise als viel geringer als der verwendete Speicherplatz angegeben wird, einschließlich Tempdb-Dateiwachstum, Dateistream-Dateien, Fehler in älteren Versionen von SQL Server ... zu viele, um sie aufzulisten. Sie könnten/sollten defensiv gegen diese Möglichkeit codieren (und auch für Offline-/nicht mehr funktionierende Dateien ... und so weiter).

Die Frage ist mit SQL Server 2014 gekennzeichnet, daher muss die veraltete Ansicht sys.sysfiles nicht verwendet werden (aus Gründen der Abwärtskompatibilität mit SQL Server 2000 ):

Ich könnte diese Abfrage schreiben als:

SELECT
    DatabaseName = DB_NAME(),
    [FileName] = DF.name,
    FileType = DF.type_desc,
    SizeMB = STR(DF.size * Factor.PagesToMB, 10, 2),
    SpaceUsedMB = STR(FP.SpaceUsed * Factor.PagesToMB, 10, 2),
    FreeSpaceMB = STR(FS.FreeSpace * Factor.PagesToMB, 10, 2),
    FreeSpacePct =  STR(Factor.ToPct * FS.FreeSpace / DF.size, 7, 4)
FROM sys.database_files AS DF
CROSS APPLY (SELECT FILEPROPERTY(DF.name, 'SpaceUsed')) AS FP (SpaceUsed)
CROSS APPLY (SELECT DF.size - FP.SpaceUsed) AS FS (FreeSpace)
CROSS JOIN  (SELECT 8e0 / 1024e0, 1e2) AS Factor (PagesToMB, ToPct);

Hauptvorteile:

  • Es trennt die Berechnungsschritte
  • Verwendet die Float-Arithmetik , um Überläufe zu vermeiden
  • STR formatiert das Ergebnis und löst beim Überlauf keinen Fehler aus
  • Der Fehler in der Frage wird dadurch nicht ausgelöst

Eine dynamische SQL-Version (um Informationen für alle Datenbanken zu sammeln):

DECLARE @SQL nvarchar(2000);

SET @SQL = N'
USE ?;

SELECT
    DatabaseName = DB_NAME(),
    [FileName] = DF.name,
    FileType = DF.type_desc,
    SizeMB = STR(DF.size * Factor.PagesToMB, 10, 2),
    SpaceUsedMB = STR(FP.SpaceUsed * Factor.PagesToMB, 10, 2),
    FreeSpaceMB = STR(FS.FreeSpace * Factor.PagesToMB, 10, 2),
    FreeSpacePct =  STR(Factor.ToPct * FS.FreeSpace / DF.size, 7, 4)
FROM sys.database_files AS DF
CROSS APPLY (SELECT FILEPROPERTY(DF.name, ''SpaceUsed'')) AS FP (SpaceUsed)
CROSS APPLY (SELECT DF.size - FP.SpaceUsed) AS FS (FreeSpace)
CROSS JOIN  (SELECT 8e0 / 1024e0, 1e2) AS Factor (PagesToMB, ToPct);
';

DECLARE @Results AS table
(
    DatabaseName sysname NOT NULL,
    [FileName] sysname NOT NULL,
    FileType nvarchar(60) NOT NULL,
    SizeMB char(10) NULL,
    SpaceUsedMB char(10) NULL,
    FreeSpaceMB char(10) NULL,
    FreeSpacePct char(7) NULL
);

INSERT @Results
EXECUTE sys.sp_MSforeachdb
    @command1 = @SQL;

SELECT R.*
FROM @Results AS R
ORDER BY R.DatabaseName; -- Or whatever

Übliche Einschränkungen zur Verwendung von sp_MSforeachdb.

15
Paul White 9

Vielleicht haben Sie vergessen, dass eine Datei 100% freien Speicherplatz haben kann?

In diesem Fall benötigen Sie DECIMAL (5,2), nicht DECIMAL (4,2).

4
Twinkles

Führen Sie die Abfrage in SSMS aus und sehen Sie sich den tatsächlichen Ausführungsplan an. Markieren Sie den Berechnungsskalar und sehen Sie sich seine Eigenschaften an (F4). Sehen Sie sich die Eigenschaft Definierte Werte an. Auf meinem System habe ich folgendes:

[Expr1008] = Scalar Operator(db_name()), [Expr1009] = 
Scalar Operator(CONVERT(int,[Expr1013]/(128.0)CONVERT_IMPLICIT(numeric(10,0),
fileproperty([ReadReceipt].[sys].[sysprufiles].[lname],'SpaceUsed'),0)
/(128.0),0)), [Expr1010] = Scalar Operator(CONVERT(varchar(8),(100.)
*CONVERT(decimal(5,2),([Expr1013]/(128.0)-CONVERT_IMPLICIT(numeric(10,0),
fileproperty([ReadReceipt].[sys].[sysprufiles].[lname],'SpaceUsed'),0)
/(128.0))/([Expr1013]/(128.0)),0),0)+'')

Dort ist viel CONVERT_IMPLICIT los. Es besteht die Möglichkeit, dass einer Ihrer DBs eine dieser Zwischenberechnungen überläuft. Ich sehe keinen Fehler auf meiner kleinen Entwicklungsbox.

Zum Debuggen würde ich nacheinander jeden Ihrer berechneten Werte auskommentieren, um zu sehen, welcher den Fehler auslöst. Dann würde ich große DBs mit WHERE herausfiltern. Wenn es für kleine DBs funktioniert, wäre das ein Hinweis. Entfernen Sie als Nächstes die Berechnung auf das Minimum und führen Sie sie nur für die größte Datenbank aus. Fügen Sie die CASTs nacheinander hinzu und vergleichen Sie die definierten Werte für Vereinbarungen, bei denen es funktioniert und bei denen es fehlschlägt.

Mein Gefühl ist, dass ein einziger CAST zu INT um die gesamte Berechnung wahrscheinlich Ihre beste Option ist.

Dieser Artikel Die Einstellung ARITHABORT hat ebenfalls eine Bedeutung.

3
Michael Green