Sql-Server
是什麼導致下面查詢中的算術溢出?
當我在 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都給出了 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 2000
sys.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);
主要優點:
動態 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
。