最近,我一直致力于执行每周备份的SQL Server作业。作为业务需求的一部分,这些数据库需要以最准确的方式从最小到最大的备份。
我已经设法用sp_msforeachdb
存储过程使用我发现的代码来帮助查找数据库大小:
DROP TABLE #temp1
CREATE TABLE #temp1
(name varchar(50),
database_size varchar(50),
Freespace varchar(50))
INSERT INTO #temp1(name,database_size,Freespace)
EXEC sp_msforeachdb
'use ?;SELECT database_name = db_name()
,database_size = ltrim(str((convert(DECIMAL(15, 2), dbsize) + convert(DECIMAL(15, 2), logsize)) * 8192 / 1048576, 15, 2))
,''unallocated space'' = ltrim(str((
CASE
WHEN dbsize >= reservedpages
THEN (convert(DECIMAL(15, 2), dbsize) - convert(DECIMAL(15, 2), reservedpages)) * 8192 / 1048576
ELSE 0
END
), 15, 2))
FROM (
SELECT dbsize = sum(convert(BIGINT, CASE
WHEN type = 0
THEN size
ELSE 0
END))
,logsize = sum(convert(BIGINT, CASE
WHEN type <> 0
THEN size
ELSE 0
END))
FROM sys.database_files
) AS files
,(
SELECT reservedpages = sum(a.total_pages)
,usedpages = sum(a.used_pages)
,pages = sum(CASE
WHEN it.internal_type IN (
202
,204
,211
,212
,213
,214
,215
,216
)
THEN 0
WHEN a.type <> 1
THEN a.used_pages
WHEN p.index_id < 2
THEN a.data_pages
ELSE 0
END)
FROM sys.partitions p
INNER JOIN sys.allocation_units a
ON p.partition_id = a.container_id
LEFT JOIN sys.internal_tables it
ON p.object_id = it.object_id
) AS partitions'
SELECT [name] AS [DB_Name], ((CAST (database_size AS DECIMAL(10,2))) - (CAST (Freespace AS DECIMAL (10,2)))) AS Used
FROM #temp1
WHERE [name] <> 'tempdb'
ORDER BY Used
我认为可以理解的是,我的公司没有将未记录的存储过程放在生产代码中,所以我一直试图找到一种解决方法。
我提出的解决这个问题的逻辑是首先创建一个临时表(#temp
)并用连续的ID#和服务器上的所有数据库ID和名称填充它{{{ 1}}表。这张桌子看起来像这样:
创建此表后,我一直在尝试遍历sys.databases
到#temp
'已使用'列中的数据库(包含数据库中使用的页数从{ {1}}中的{1}}。在此之后,我已经有了一个功能光标,它将通过UPDATE
并按照我们需要的顺序运行备份SUM(used_pages)
。所以基本上在这个3部分过程中,我只是坚持填充sys.allocation_units
中已用空间的步骤。
为了避免可能是游标的恐怖,我原本试图使用#temp
循环来填充我的#temp表。这就是我想出的:
ORDER BY
我无法让这个工作,因为从我的理解,变量不能在动态SQL中的#temp
语句和Tom Staab使用WHILE
提出的solution中工作。对我不起作用。基本上最终发生的是每次执行代码时,引用的第一个数据库中使用的页面数量将填充到我的DECLARE @sql NVARCHAR(4000);
DECLARE @BigSQL NVARCHAR(4000);
DECLARE @dbName VARCHAR(100);
SET @sql = 'UPDATE tempdb..#temp
SET Used = (SELECT SUM(a.used_pages)
FROM sys.partitions p
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id
LEFT JOIN sys.internal_tables it ON p.object_id = it.object_id)';
SET @dbName = N'master'
DECLARE @db_name VARCHAR(100)
,@db_size DECIMAL(15, 2)
,@db_freespace DECIMAL(15, 2);
IF object_id ('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
--index me
CREATE TABLE #temp (
ID INT IDENTITY(1,1),
DB_ID INT,
[Name] VARCHAR(50)
,Used VARCHAR(50)
)
INSERT INTO #temp (DB_ID, [Name])
SELECT database_id, [Name]
FROM sys.databases
--#temp check 1
SELECT *
FROM #temp
DECLARE @min INT
, @max INT
, @cur INT;
DECLARE @executionString VARCHAR(MAX),
@DB VARCHAR(50);
SELECT @min = MIN(ID),
@max = MAX(ID),
@cur = MIN(ID)
FROM #temp;
WHILE (@cur <= @max)
BEGIN
SELECT @dbName = [Name] FROM sys.databases WHERE database_id = @cur;
SET @BigSQL = 'USE ' + @dbName + '; EXEC sp_executesql N'''+ @sql + '''';
EXEC(@BigSQL);
SET @cur = @cur + 1;
END
--#temp check 2
SELECT *
FROM #temp
表中的每个数据库。因此,我认为问题可能在循环中,并且过渡到游标可能会有所帮助。我写了以下内容:
USE ['database']
我遇到的问题是sp_executesql
表中的第一个数据库(本例中为master)只是反复评估,直到我取消查询。如果我在取消后#temp
,则表格的第一行会正确填充,但所有其余行仍为DECLARE @used INTEGER
, @Name VARCHAR(100);
IF object_id ('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
--index me
CREATE TABLE #temp (
ID INT IDENTITY(1,1)
, DB_ID INT
, [Name] VARCHAR(50)
,Used VARCHAR(50)
)
INSERT INTO #temp (DB_ID, [Name])
SELECT database_id, [Name]
FROM sys.databases
--#temp check 1
SELECT *
FROM #temp
DECLARE temp_populate CURSOR FOR
SELECT [Name]
FROM #temp
OPEN temp_populate
FETCH NEXT FROM temp_populate
INTO @Name
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @used = SUM(used_pages)
FROM sys.allocation_units
IF((SELECT Used FROM #temp WHERE [Name] = @Name) IS NULL)
UPDATE #temp
SET Used = @used
WHERE [Name] = @Name;
ELSE
PRINT ''
END
CLOSE temp_populate
DEALLOCATE temp_populate
--#temp check 2
SELECT *
FROM #temp
。
我知道这是一篇非常广泛的帖子,但我现在已经开始研究这几天了,老实说,我会欣赏一双新鲜的眼睛或者对SQL更有经验的人,他们可以完全针对什么提出不同的方法我正在努力做到。非常感谢阅读所有这些!
答案 0 :(得分:0)
答案 1 :(得分:0)
尝试此查询以获取数据库的大小
CREATE TABLE #temp (
ID INT IDENTITY(1,1)
, DB_ID INT
, [Name] VARCHAR(50)
, Used DECIMAL(8,2)
)
INSERT INTO #temp (DB_ID, [Name], Used)
SELECT
MF.database_id
,database_name = DB_NAME(database_id)
, total_size_mb = CAST(SUM(size) * 8. / 1024 AS DECIMAL(8,2))
FROM sys.master_files MF
GROUP BY database_id
ORDER BY total_size_mb DESC
--#temp check 1
SELECT *
FROM #temp
答案 2 :(得分:0)
不要重新发明轮子 - 特别是如果你没有重要的tsql技能。使用Ola的解决方案here - 这些解决方案被广泛使用,写得很好并且备受推崇。