临时表填充WHILE / Cursor

时间:2017-10-24 13:15:11

标签: sql-server database while-loop cursor dynamic-programming

最近,我一直致力于执行每周备份的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}}表。这张桌子看起来像这样:

First couple rows of #temp

创建此表后,我一直在尝试遍历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更有经验的人,他们可以完全针对什么提出不同的方法我正在努力做到。非常感谢阅读所有这些!

3 个答案:

答案 0 :(得分:0)

  

我从来没有能够让这个工作,因为从我所学到的,   USE ['database']不能在动态SQL中使用

您可以在dynamic SQL

中使用USE

答案 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 - 这些解决方案被广泛使用,写得很好并且备受推崇。