Sql-Server

是什麼導致下面查詢中的算術溢出?

  • May 8, 2020

當我在 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.sysfilesFILEPROPERTY都給出了 8K 頁面的數量,而不是 MB,並且要將 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,因此無需使用已棄用的視圖(為了與 SQL Server 2000sys.sysfiles向後兼容):

我可能會將這個查詢寫成:

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

引用自:https://dba.stackexchange.com/questions/139986