web-dev-qa-db-ja.com

以下のクエリで算術オーバーフローを引き起こしているものは何ですか?

mY_Databaseで次のクエリを実行すると

select * from sys.sysfiles

次の結果が得られます。

enter image description here

しかし、取得した空き領域の割合を取得する動的クエリを実行すると、次のようになります。

メッセージ8115、レベル16、状態7、行93数値をデータ型数値に変換する算術オーバーフローエラー。

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

算術オーバーフローの理由を見つけるのに苦労しています。なぜそれが起こっているのですか?

なぜ128で割りますか? sys.sysfiles[〜#〜] fileproperty [〜#〜] の両方がMBではなく8Kページの数を提供し、8KページからMBで128で割ります ここで説明しています

なぜ動的なのですか?

以下の例でわかるように、実際には値 sp_ForEachDBを使用して各データベースから を取得しているためです。

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

算術オーバーフローの理由を見つけるのに苦労しています。なぜそれが起こっているのですか?

おそらくメタデータが、コードが処理できないいくつかの予期しない値を返しています。例えば:

_-- 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;
_

...計算された(負の!)値がdecimal(5,2)に収まらないため、質問で言及されたエラーを返します。

Tempdbファイルの増加、ファイルストリームファイル、SQL Serverの以前のバージョンのバグなど、使用するスペースよりもはるかに小さいサイズで報告される理由はいくつかあります。あなたはこの可能性に対して防御的にコーディングすることができます/すべきです(そしてオフライン/破棄されたファイルについても...など)。

質問にはSQL Server 2014のタグが付いているため、廃止予定の _sys.sysfiles_ ビューを使用する必要はありません(SQL Serverとの下位互換性のために2000):

このクエリは次のように書くことができます。

_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);
_

主な利点:

  • 計算ステップを分離します
  • float演算を使用してオーバーフローを回避します
  • STR 結果をフォーマットし、オーバーフロー時にエラーを発生させません
  • 質問のエラーがスローされることはありません

動的SQLバージョン(すべてのデータベースの情報を収集するため):

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

通常の警告 _sp_MSforeachdb_の使用について。

15
Paul White 9

ファイルに100%の空き容量があることを忘れたのではないでしょうか?

その場合、DECIMAL(4,2)ではなくDECIMAL(5,2)が必要です。

4
Twinkles

SSMSでクエリを実行し、実際の実行プランを確認します。 Compute Scalarを強調表示して、そのプロパティを確認します(F4)。 [定義された値]プロパティを確認します。私のシステムではこれを得ました:

[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)+'')

たくさんのCONVERT_IMPLICITが進行中です。 DBの1つがこれらの中間計算の1つをオーバーフローしている可能性があります。小さな開発ボックスにエラーは表示されません。

デバッグするには、計算された各値を順番にコメント化して、エラーをスローしているものを確認します。次に、WHEREを使用して大きなDBを除外します。それが小さなDBで機能する場合、それは手掛かりになります。次に、計算を最小値まで減らし、最大のDBに対してのみ実行します。 CASTを一度に1つずつ追加し、定義された値を比較して、機能する場所と失敗する場所を調整します。

私の考えでは、計算全体でINTへの単一のCASTが最良のオプションである可能性があります。

この記事 状態ARITHABORT設定にも影響があります。

3
Michael Green