【问题标题】:Tempdb Full When Querying Distinct Count Of All Tables查询所有表的不同计数时 Tempdb Full
【发布时间】:2015-07-07 10:22:49
【问题描述】:

原始问题

我创建了一个自定义脚本,用于将远程 SQL 服务器中的数据检索到我们办公室的本地副本中。我在脚本中遇到了一些问题,其中选定的表插入了两次数据,从而创建了重复项。我知道对于所有数据库中的所有表应该没有重复。

这个问题让我怀疑其他表可能在历史上也有这个问题,因此我想验证一下。

解决方案

我创建了一个 SQL 脚本,用于将所有列的计数和不同计数插入到我们服务器上所有数据库(不包括 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

其工作原理的概述是表变量@AllTables 使用 sp_msforeachdb 和 INFORMATION_SCHEMA.TABLES 列出所有数据库中的所有表(有 16537 个表)。表游标用于存储所有非系统条目,然后我使用动态 SQL 进行计数和不同计数,这些计数存储在另一个表变量 @ResultsTable 中。

此解决方案的问题

当我运行这个查询时,它会运行大约 3 分钟,然后抛出一个错误,提示 tempdb PRIMARY 文件组已满:

我是我自己的 DBA,我使用Brent Ozar's guide 来设置我的 SQL 服务器实例,我的 tempdb 设置有 8 x 3GB mdf/ndf 文件(服务器有 8 个核心):

这些文件在“常规”属性下显示为有 23997MB 可用。

我的问题

  1. 如果我有大约 24GB 的 tempdb 可用空间,为什么这个相对简单的查询会用完 tempdb 空间?
  2. 是否有更好/更有效的方法来获取所有数据库中所有表的计数和不同计数?

【问题讨论】:

  • 正在处理多少数据?如果您区分具有许多不同值的数据,那么所有这些值最终都会出现在临时工作表中。解决此问题的方法很重要。您需要分块处理表格。
  • 感谢您的回复。您指的是子查询: SELECT DISTINCT * FROM ' + @TableFullName 吗?是放在 tempdb 中的吗?显然它处理了数千个表,所以如果它缓存所有这些表(我原以为每次循环迭代都会清除缓存?)那么它将是数百 GB 的数据。
  • 循环的每次迭代是否都没有从缓存中删除子查询?我会在谷歌上搜索一下。
  • 你在说什么缓存?当每个查询结束时,tempdb 的使用就结束了。一定有什么大问题。
  • 我承认我对查询缓存的了解非常有限,但为什么 SQL Server 会为循环内运行的每个子查询缓存所有表(以及整个表)?

标签: database sql-server-2012 tempdb


【解决方案1】:

在添加 TempDb 文件之前,您应该始终考虑争用。添加 7 个额外的 TempDb 文件并没有真正的帮助。

如果我有大约 24GB 的 tempdb 可用空间,为什么相对而言 简单查询的 tempdb 空间不足?

不,不应该。但是您确定您不是在处理大量数据,或者您没有在 SQL 上运行其他进程吗?游标、临时表甚至表变量都广泛使用 TempDb。检查哪个对象消耗更多的 TempDb 空间:

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

所以,如果您的用户和内部对象更多,那么很明显,由于游标和 SQL Server 内部使用(例如:中间表、哈希连接、哈希聚合等),您的 TempDb 空间较低

是否有更好/更有效的方法来获得计数和区分 所有数据库中所有表的计数?

您可以使用下面的代码来获取所有数据库中所有表的计数

  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**

如果您不确定统计信息更新,请使用以下查询来获取所有表的计数 注意:替换不需要统计信息的数据库

    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

同样,您可以使用以下查询在所有数据库的所有表中进行区分

    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

【讨论】:

  • 感谢您回复 Anuj。虽然我不声称自己是专家,但我对 tempdb 的了解足以了解争用,并且我已经按照support.microsoft.com/en-us/kb/2154845 的 Microsoft 指导为 8 个处理器内核设置了 8 个 tempdb 文件。在这种情况下,问题是由每次在 tempdb 中缓存的 SELECT DISTINCT 引起的。由于我在我们的服务器实例中的每个数据库上都执行此查询,因此没有多少 tempdb 重新配置足以做到这一点。但是,我的另一个经验领域是 VB,所以我将编写一个 VBA 程序来执行此操作并在此处发布答案。
  • 我还想指出,您提供的用于检查哪些对象消耗 tempdb 空间的代码非常有帮助,谢谢(我已经在稍后提供的答案中使用了它)。但是,您为获取每个表中的行而提供的代码并不是很有帮助,因为 (1) 它没有为我提供不同的计数,这是我的问题的要求,并且 (2) 它基于统计数据和不如 COUNT(*) 准确。
  • @Quarcheek 我已经用更新的查询编辑了我的答案
  • 感谢您一直以来对 Anuj 的支持。最后事实证明,使用您在原始答案中给我的查询帮助我找到了问题的根源。实际上,我真的很喜欢您的独特查询,它比我自己的要简洁得多。我不知道我可以将 sp_msforeachdb 执行到这样的表中,或者您可以将 sp_msforeachtable 嵌套在 sp_msforeachdb 中。非常聪明!用光标救了我!
  • 最后的问题是结果开始附近有一个大表(唯一一个大于 24GB 的表),这导致 tempdb 抛出错误。一旦我对结果进行排序,我就发现了这个问题。一旦我排除了这个表,它目前正在通过最后几个数据库运行,所以再次感谢您的帮助。自编辑以来,我已将您的答案标记为已接受的答案,因为您已成功回答了我的两个问题。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多