mY_Databaseで次のクエリを実行すると
select * from sys.sysfiles
次の結果が得られます。
しかし、取得した空き領域の割合を取得する動的クエリを実行すると、次のようになります。
メッセージ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
算術オーバーフローの理由を見つけるのに苦労しています。なぜそれが起こっているのですか?
おそらくメタデータが、コードが処理できないいくつかの予期しない値を返しています。例えば:
_-- 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);
_
主な利点:
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
_の使用について。
ファイルに100%の空き容量があることを忘れたのではないでしょうか?
その場合、DECIMAL(4,2)ではなくDECIMAL(5,2)が必要です。
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設定にも影響があります。