温度 Table 填充 WHILE/Cursor
Temp Table Populate WHILE/Cursor
最近我一直在处理 SQL 服务器作业,该作业执行我们的每周备份。作为业务需求的一部分,这些数据库需要以最准确的方式从小到大进行备份。
我已经使用 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
我认为可以理解,我的公司不会将未记录的存储过程放入生产代码中,因此我一直在努力寻找解决方法。
我想出的解决这个问题的逻辑是首先创建一个临时的 table (#temp
) 并用连续的 ID# 和所有数据库 ID 和名称填充它来自 sys.databases
table 的服务器。 table 最终看起来像这样:
First couple rows of #temp
创建此 table 后,我一直在尝试遍历 #temp
中的数据库到 UPDATE
'Used' 列(包含页数从 SUM(used_pages)
in sys.allocation_units
中提取的数据库中使用)。在此之后,我已经有了一个功能游标,它将使用 ORDER BY
按照我们需要的顺序遍历 #temp
和 运行 备份。所以基本上在这 3 个部分的过程中,我只是停留在填充 #temp
中使用的 space 的步骤上。
为了避免游标带来的恐惧,我最初尝试使用 WHILE
循环来填充我的#temp table。这是我想出的:
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
我一直无法让它工作,因为据我所知,变量在动态 SQL 和 Tom 提出的 solution 中的 USE ['database']
语句中不起作用使用 sp_executesql
的 Staab 对我不起作用。基本上最终发生的事情是每次我执行代码时,第一个引用的数据库中使用的页数将填充到我的 #temp
table 中的每个数据库。因此,我认为问题可能出在循环中,过渡到游标可能会有所帮助。我写了以下内容:
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
我在这里遇到的问题是 #temp
table 中的第一个数据库(在本例中为 master)只是一遍又一遍地评估,直到我取消查询。如果我在取消后 SELECT * FROM #temp
,table 的第一行会正确填充,但其余所有内容仍为 NULL
。
我知道这是相当广泛的 post,但我已经为此工作了几天,老实说,我希望能有新的一双眼睛或对 [=58= 更有经验的人] 谁可以完全针对我正在尝试做的事情提出不同的方法。非常感谢您阅读所有这些内容!
I was never able to get this to work because from what I've learned,
USE ['database'] can't be used in dynamic SQL
您可以在 dynamic SQL
中使用 USE
试试这个查询来获取数据库的大小
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
不要重新发明轮子 - 特别是如果您没有重要的 tsql 技能。使用 Ola 的解决方案 here - 这些解决方案被广泛使用、编写精良且备受推崇。
最近我一直在处理 SQL 服务器作业,该作业执行我们的每周备份。作为业务需求的一部分,这些数据库需要以最准确的方式从小到大进行备份。
我已经使用 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
我认为可以理解,我的公司不会将未记录的存储过程放入生产代码中,因此我一直在努力寻找解决方法。
我想出的解决这个问题的逻辑是首先创建一个临时的 table (#temp
) 并用连续的 ID# 和所有数据库 ID 和名称填充它来自 sys.databases
table 的服务器。 table 最终看起来像这样:
First couple rows of #temp
创建此 table 后,我一直在尝试遍历 #temp
中的数据库到 UPDATE
'Used' 列(包含页数从 SUM(used_pages)
in sys.allocation_units
中提取的数据库中使用)。在此之后,我已经有了一个功能游标,它将使用 ORDER BY
按照我们需要的顺序遍历 #temp
和 运行 备份。所以基本上在这 3 个部分的过程中,我只是停留在填充 #temp
中使用的 space 的步骤上。
为了避免游标带来的恐惧,我最初尝试使用 WHILE
循环来填充我的#temp table。这是我想出的:
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
我一直无法让它工作,因为据我所知,变量在动态 SQL 和 Tom 提出的 solution 中的 USE ['database']
语句中不起作用使用 sp_executesql
的 Staab 对我不起作用。基本上最终发生的事情是每次我执行代码时,第一个引用的数据库中使用的页数将填充到我的 #temp
table 中的每个数据库。因此,我认为问题可能出在循环中,过渡到游标可能会有所帮助。我写了以下内容:
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
我在这里遇到的问题是 #temp
table 中的第一个数据库(在本例中为 master)只是一遍又一遍地评估,直到我取消查询。如果我在取消后 SELECT * FROM #temp
,table 的第一行会正确填充,但其余所有内容仍为 NULL
。
我知道这是相当广泛的 post,但我已经为此工作了几天,老实说,我希望能有新的一双眼睛或对 [=58= 更有经验的人] 谁可以完全针对我正在尝试做的事情提出不同的方法。非常感谢您阅读所有这些内容!
I was never able to get this to work because from what I've learned, USE ['database'] can't be used in dynamic SQL
您可以在 dynamic SQL
中使用USE
试试这个查询来获取数据库的大小
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
不要重新发明轮子 - 特别是如果您没有重要的 tsql 技能。使用 Ola 的解决方案 here - 这些解决方案被广泛使用、编写精良且备受推崇。