查询所有表的非重复计数时 Tempdb 已满
Tempdb Full When Querying Distinct Count Of All Tables
原问题
我创建了一个自定义脚本,用于将数据从远程 SQL 服务器检索到我们办公室的本地副本中。我在脚本中遇到了一些问题,其中选定的 tables 将一些数据插入了两次,从而创建了重复项。我知道对于所有数据库中的所有 tables 应该没有重复项。
这个问题让我怀疑其他 table 历史上可能有过这个问题,因此我想验证一下。
解决方案
我创建了一个 SQL 脚本,用于将所有列的计数和非重复计数插入到 table 中,用于我们服务器上的所有数据库(不包括 4 个系统数据库):
DECLARE @TableFullName AS NVARCHAR(MAX)
DECLARE @SQLQuery AS NVARCHAR(MAX)
DECLARE @TableHasDuplicates AS BIT
DECLARE @TempTableRowCount AS INT
DECLARE @ResultsTable TABLE ([CompleteTableName] NVARCHAR(200), [CountAll] INT, [CountDistinct] INT)
DECLARE @CountAll INT
DECLARE @CountDistinct INT
SET NOCOUNT ON
DECLARE @AllTables TABLE ([CompleteTableName] NVARCHAR(200))
INSERT INTO @AllTables ([CompleteTableName])
EXEC sp_msforeachdb 'SELECT ''['' + [TABLE_CATALOG] + ''].['' + [TABLE_SCHEMA] + ''].['' + [TABLE_NAME] + '']'' FROM [?].INFORMATION_SCHEMA.TABLES'
SET NOCOUNT OFF;
DECLARE [table_cursor] CURSOR FOR
(SELECT *
FROM @AllTables
WHERE [CompleteTableName] NOT LIKE '%master%' AND [CompleteTableName] NOT LIKE '%msdb%' AND [CompleteTableName] NOT LIKE '%tempdb%' AND [CompleteTableName] NOT LIKE '%model%');
OPEN [table_cursor]
PRINT N'There were ' + CAST(@CountAll AS NVARCHAR(10)) + ' tables with potential duplicate data'
FETCH NEXT FROM [table_cursor]
INTO @TableFullName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQLQuery = 'SELECT @CntAll = COUNT(*) FROM ' + @TableFullName + ' SELECT @CntDistinct = COUNT(*) FROM (SELECT DISTINCT * FROM ' + @TableFullName + ') AS [sq] IF @CntAll > @CntDistinct SELECT @BitResult=1 ELSE SELECT @BitResult=0';
EXEC sp_executesql @SQLQuery, N'@BitResult BIT OUTPUT, @CntAll INT OUTPUT, @CntDistinct INT OUTPUT', @BitResult = @TableHasDuplicates OUTPUT, @CntAll = @CountAll OUTPUT, @CntDistinct = @CountDistinct OUTPUT;
IF @TableHasDuplicates = 1
BEGIN
INSERT INTO @ResultsTable ([CompleteTableName], [CountAll], [CountDistinct])
SELECT @TableFullName, @CountAll, @CountDistinct
END;
FETCH NEXT FROM [table_cursor]
INTO @TableFullName
END
CLOSE [table_cursor];
DEALLOCATE [table_cursor];
SELECT *
FROM @ResultsTable
table 变量 @AllTables 使用 sp_msforeachdb 和 INFORMATION_SCHEMA.TABLES 来列出所有数据库中的所有 table(有 16537 tables)。 table 游标用于存储所有非系统条目,然后我使用动态 SQL 进行计数和非重复计数,存储在另一个 table 变量@ResultsTable.
这个解决方案的问题
当我 运行 这个查询时,它会 运行 大约 3 分钟然后抛出一个错误,指出 tempdb PRIMARY 文件组已满:
我是我自己的 DBA,我使用 Brent Ozar's guide 来设置我的 SQL 服务器实例,我的 tempdb 设置了 8 x 3GB mdf/ndf 文件(服务器有8个核心):
这些文件在 'General' 属性下显示有 23997MB 可用空间。
我的问题
- 如果我有大约 24GB 的可用 tempdb space,为什么这个相对简单的查询 运行 宁出 tempdb space?
- 是否有一种 better/more 有效的方法来获取所有数据库中所有 table 的计数和非重复计数?
在添加 TempDb 文件之前,您应该始终考虑争用。添加 7 个额外的 TempDb 文件并没有多大帮助。
If I have circa 24GB of tempdb free space, why is this relatively
simple query running out of tempdb space?
不,不应该。但是您确定您没有处理大量数据,或者您在 SQL 上没有其他进程 运行?游标、Temp tables 甚至 table 变量都广泛使用 TempDb。检查哪个对象消耗更多的 TempDb space:
SELECT
SUM (user_object_reserved_page_count)*8 as usr_obj_kb,
SUM (internal_object_reserved_page_count)*8 as internal_obj_kb,
SUM (version_store_reserved_page_count)*8 as version_store_kb,
SUM (unallocated_extent_page_count)*8 as freespace_kb,
SUM (mixed_extent_page_count)*8 as mixedextent_kb
FROM sys.dm_db_file_space_usage
因此,如果您的用户和内部对象更多,那么这显然意味着您的 TempDb space 因为游标和 SQL 服务器内部使用(例如:中间 table s、哈希连接、哈希聚合等)
Is there a better/more efficiency way of getting a count and distinct
count of all tables in all databases?
您可以使用以下代码获取所有数据库中所有 table 的计数
DECLARE @Stats TABLE (DBNAME VARCHAR(40), NAME varchar(200), Rows INT)
INSERT INTO @Stats
EXECUTE sp_MSForEachDB
'USE ?; SELECT DB_NAME()AS DBName,
sysobjects.Name
, sysindexes.Rows
FROM
sysobjects
INNER JOIN sysindexes
ON sysobjects.id = sysindexes.id
WHERE
type = ''U''
AND sysindexes.IndId < 2'
SELECT * FROM @Stats
我在TempDb recommendation上写了一篇文章;我建议您阅读它以了解可能影响 TempDb 的对象以及如何解决它的常见问题。理想情况下,您的 TempDb 总大小应根据观察结果计算,在您的情况下 > 24 GB。
** 编辑 1**
如果您不确定统计更新,请使用以下查询来获取所有 table 的计数
注意:替换您不需要统计信息的数据库
DECLARE @ServerStats TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO @ServerStats
exec sp_msforeachdb @command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable @command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM ? ;
''
end
', @replacechar = '#'
SELECT * FROM @ServerStats
类似地,对于具有以下查询
的所有数据库,您可以在所有 table 中取不同
DECLARE @ServerStatsDistinct TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO @ServerStatsDistinct
exec sp_msforeachdb @command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable @command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM (
SELECT DISTINCT *
FROM ?
) a ;
''
end
', @replacechar = '#'
SELECT * FROM @ServerStatsDistinct
原问题
我创建了一个自定义脚本,用于将数据从远程 SQL 服务器检索到我们办公室的本地副本中。我在脚本中遇到了一些问题,其中选定的 tables 将一些数据插入了两次,从而创建了重复项。我知道对于所有数据库中的所有 tables 应该没有重复项。
这个问题让我怀疑其他 table 历史上可能有过这个问题,因此我想验证一下。
解决方案
我创建了一个 SQL 脚本,用于将所有列的计数和非重复计数插入到 table 中,用于我们服务器上的所有数据库(不包括 4 个系统数据库):
DECLARE @TableFullName AS NVARCHAR(MAX)
DECLARE @SQLQuery AS NVARCHAR(MAX)
DECLARE @TableHasDuplicates AS BIT
DECLARE @TempTableRowCount AS INT
DECLARE @ResultsTable TABLE ([CompleteTableName] NVARCHAR(200), [CountAll] INT, [CountDistinct] INT)
DECLARE @CountAll INT
DECLARE @CountDistinct INT
SET NOCOUNT ON
DECLARE @AllTables TABLE ([CompleteTableName] NVARCHAR(200))
INSERT INTO @AllTables ([CompleteTableName])
EXEC sp_msforeachdb 'SELECT ''['' + [TABLE_CATALOG] + ''].['' + [TABLE_SCHEMA] + ''].['' + [TABLE_NAME] + '']'' FROM [?].INFORMATION_SCHEMA.TABLES'
SET NOCOUNT OFF;
DECLARE [table_cursor] CURSOR FOR
(SELECT *
FROM @AllTables
WHERE [CompleteTableName] NOT LIKE '%master%' AND [CompleteTableName] NOT LIKE '%msdb%' AND [CompleteTableName] NOT LIKE '%tempdb%' AND [CompleteTableName] NOT LIKE '%model%');
OPEN [table_cursor]
PRINT N'There were ' + CAST(@CountAll AS NVARCHAR(10)) + ' tables with potential duplicate data'
FETCH NEXT FROM [table_cursor]
INTO @TableFullName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQLQuery = 'SELECT @CntAll = COUNT(*) FROM ' + @TableFullName + ' SELECT @CntDistinct = COUNT(*) FROM (SELECT DISTINCT * FROM ' + @TableFullName + ') AS [sq] IF @CntAll > @CntDistinct SELECT @BitResult=1 ELSE SELECT @BitResult=0';
EXEC sp_executesql @SQLQuery, N'@BitResult BIT OUTPUT, @CntAll INT OUTPUT, @CntDistinct INT OUTPUT', @BitResult = @TableHasDuplicates OUTPUT, @CntAll = @CountAll OUTPUT, @CntDistinct = @CountDistinct OUTPUT;
IF @TableHasDuplicates = 1
BEGIN
INSERT INTO @ResultsTable ([CompleteTableName], [CountAll], [CountDistinct])
SELECT @TableFullName, @CountAll, @CountDistinct
END;
FETCH NEXT FROM [table_cursor]
INTO @TableFullName
END
CLOSE [table_cursor];
DEALLOCATE [table_cursor];
SELECT *
FROM @ResultsTable
table 变量 @AllTables 使用 sp_msforeachdb 和 INFORMATION_SCHEMA.TABLES 来列出所有数据库中的所有 table(有 16537 tables)。 table 游标用于存储所有非系统条目,然后我使用动态 SQL 进行计数和非重复计数,存储在另一个 table 变量@ResultsTable.
这个解决方案的问题
当我 运行 这个查询时,它会 运行 大约 3 分钟然后抛出一个错误,指出 tempdb PRIMARY 文件组已满:
我是我自己的 DBA,我使用 Brent Ozar's guide 来设置我的 SQL 服务器实例,我的 tempdb 设置了 8 x 3GB mdf/ndf 文件(服务器有8个核心):
这些文件在 'General' 属性下显示有 23997MB 可用空间。
我的问题
- 如果我有大约 24GB 的可用 tempdb space,为什么这个相对简单的查询 运行 宁出 tempdb space?
- 是否有一种 better/more 有效的方法来获取所有数据库中所有 table 的计数和非重复计数?
在添加 TempDb 文件之前,您应该始终考虑争用。添加 7 个额外的 TempDb 文件并没有多大帮助。
If I have circa 24GB of tempdb free space, why is this relatively simple query running out of tempdb space?
不,不应该。但是您确定您没有处理大量数据,或者您在 SQL 上没有其他进程 运行?游标、Temp tables 甚至 table 变量都广泛使用 TempDb。检查哪个对象消耗更多的 TempDb space:
SELECT
SUM (user_object_reserved_page_count)*8 as usr_obj_kb,
SUM (internal_object_reserved_page_count)*8 as internal_obj_kb,
SUM (version_store_reserved_page_count)*8 as version_store_kb,
SUM (unallocated_extent_page_count)*8 as freespace_kb,
SUM (mixed_extent_page_count)*8 as mixedextent_kb
FROM sys.dm_db_file_space_usage
因此,如果您的用户和内部对象更多,那么这显然意味着您的 TempDb space 因为游标和 SQL 服务器内部使用(例如:中间 table s、哈希连接、哈希聚合等)
Is there a better/more efficiency way of getting a count and distinct count of all tables in all databases?
您可以使用以下代码获取所有数据库中所有 table 的计数
DECLARE @Stats TABLE (DBNAME VARCHAR(40), NAME varchar(200), Rows INT)
INSERT INTO @Stats
EXECUTE sp_MSForEachDB
'USE ?; SELECT DB_NAME()AS DBName,
sysobjects.Name
, sysindexes.Rows
FROM
sysobjects
INNER JOIN sysindexes
ON sysobjects.id = sysindexes.id
WHERE
type = ''U''
AND sysindexes.IndId < 2'
SELECT * FROM @Stats
我在TempDb recommendation上写了一篇文章;我建议您阅读它以了解可能影响 TempDb 的对象以及如何解决它的常见问题。理想情况下,您的 TempDb 总大小应根据观察结果计算,在您的情况下 > 24 GB。
** 编辑 1**
如果您不确定统计更新,请使用以下查询来获取所有 table 的计数 注意:替换您不需要统计信息的数据库
DECLARE @ServerStats TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO @ServerStats
exec sp_msforeachdb @command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable @command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM ? ;
''
end
', @replacechar = '#'
SELECT * FROM @ServerStats
类似地,对于具有以下查询
的所有数据库,您可以在所有 table 中取不同 DECLARE @ServerStatsDistinct TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO @ServerStatsDistinct
exec sp_msforeachdb @command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable @command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM (
SELECT DISTINCT *
FROM ?
) a ;
''
end
', @replacechar = '#'
SELECT * FROM @ServerStatsDistinct