在查询所有表的不同计数时,Tempdb已满

时间:2015-07-07 10:22:50

标签: database sql-server-2012 tempdb

原始问题

我创建了一个自定义脚本,用于将数据从远程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文件组已满: Error Message tempdb PRIMARY filegroup full

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

这些文件显示在' General'下可用23997MB。属性。

我的问题

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

1 个答案:

答案 0 :(得分:2)

在添加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