2015-07-07 3 views
0

ОРИГИНАЛ ПРОБЛЕМАTempdb Полное При запросе Distinct Count всех таблиц

Я создал специальный скрипт для извлечения данных из удаленного сервера 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 ОСНОВНОЙ файловая группа полна: Error Message tempdb PRIMARY filegroup full

Я мой собственный DBA, и я использовал Brent Ozar's guide для создания моего экземпляра SQL-сервера, и мой Tempdb устанавливается с 8 х 3GB/NDF МДФ файлов (сервер имеет 8 ядер): tempdb setup

Эти файлы имеют доступ к 23997 МБ в свойствах «Общие».

МОИ ВОПРОСЫ

  1. Если я около 24 Гб свободного пространства TempDb, почему это относительно простой запрос кончаются TempDb пространства?
  2. Есть ли лучший/более эффективный способ получения счета и отличного подсчета всех таблиц во всех базах данных?
+0

Сколько данных обрабатывается? Если вы отличаетесь данными, имеющими много разных значений, то все эти значения оказываются во временной рабочей таблице. Обходные пути для этого нетривиальны. Вам нужно будет обработать таблицу в кусках. – usr

+0

Спасибо, что ответили usr. Вы имеете в виду подзапрос: SELECT DISTINCT * FROM '+ @TableFullName? Это помещено в tempdb? Очевидно, он обрабатывает тысячи таблиц, поэтому, если он кэширует все эти таблицы (я бы подумал, что кеш будет очищен каждой итерацией цикла?), Тогда это будут сотни ГБ данных. – Quarcheek

+0

Возможно, это так. – usr

ответ

2

Перед добавлением файла TempDb вы должны всегда рассматривать конкуренцию. Добавление 7 дополнительных файлов TempDb действительно не поможет.

Если у меня есть около 24 Гб свободного пространства TempDb, почему эта относительно простого запроса кончается TempDb пространства?

Нет, не следует. Но уверены ли вы, что вы не имеете дело с большим количеством данных или у вас нет другого процесса, выполняющегося на SQL? Курсоры, таблицы Temp и даже табличные переменные широко используют 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 

Таким образом, если пользователь и внутренние объекты более ясно означает, что у вас низкий TempDb пространство из-за курсоров и внутреннего использования SQL Server (Ex: промежуточные столы, Хэш соединение, Hash агрегация и т.д.)

есть ли лучше/эффективность способа получения счета и отчетливое количества всех таблиц во всех базах данных?

Вы можете использовать ниже код, чтобы получить количество всех таблиц во всех базах данных

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 ГБ.

** Edit 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 
+0

Спасибо, что ответили Anuj. Хотя я не утверждаю, что являюсь экспертом, я достаточно знаю о том, что tempdb знает о конфликте, и я установил 8 файлов tempdb для 8 процессорных ядер, следуя указаниям Microsoft на странице https://support.microsoft.com/en-us/ кб/2154845. В этом случае проблема вызвана SELECT DISTINCT, которая кэшируется каждый раз в tempdb. Поскольку я беру этот запрос на каждую базу данных в нашем экземпляре сервера, для этого не потребуется никаких изменений в конфигурации tempdb. Тем не менее, моя другая область опыта - VB, поэтому я напишу программу VBA для этого и отправлю ответ здесь. – Quarcheek

+0

Я также хотел указать, что код, который вы предоставили для проверки того, какие объекты для использования пространства tempdb очень полезны, спасибо (я использовал это в ответе, который я вскоре предоставит). Однако код, который вы предоставили для получения строк в каждой таблице, не очень полезен, поскольку (1) он не предоставил мне отчетный счет, который является требованием моего вопроса, и (2) он основан на статистике и не так точно, как COUNT (*). – Quarcheek

+0

@Quarcheek Я отредактировал свой ответ с обновленным запросом –