Mar*_*lli 5 sql-server filegroups dynamic-sql database-size sql-server-2014
当我在 MY_Database 上运行以下查询时
select * from sys.sysfiles
Run Code Online (Sandbox Code Playgroud)
我得到以下结果:
但是当我运行一个动态查询来获取可用空间的百分比时,我得到:
消息 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
Run Code Online (Sandbox Code Playgroud)
我正在努力寻找算术溢出的原因。为什么会这样?
为什么要除以 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
Run Code Online (Sandbox Code Playgroud)
Pau*_*ite 15
我正在努力寻找算术溢出的原因。为什么会这样?
元数据很可能会返回一些您的代码无法处理的意外值。例如:
-- 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;
Run Code Online (Sandbox Code Playgroud)
...返回问题中提到的错误,因为计算出的(负!)值不适合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);
Run Code Online (Sandbox Code Playgroud)
主要优势:
动态 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
Run Code Online (Sandbox Code Playgroud)
平常注意事项有关使用sp_MSforeachdb
。