Ход выполнения инструкции SELECT INTO

14

Наш поток ETL имеет длительный оператор SELECT INTO, который создает таблицу на лету и наполняет ее несколькими сотнями миллионов записей.

Заявление выглядит примерно так SELECT ... INTO DestTable FROM SrcTable

В целях мониторинга мы хотели бы получить приблизительное представление о ходе выполнения этого оператора во время его выполнения (приблизительное количество строк, записанное число байтов или подобное).

Мы попробовали следующее безрезультатно:

-- Is blocked by the SELECT INTO statement:
select count(*) from DestTable with (nolock)

-- Returns 0, 0:
select rows, rowmodctr
from sysindexes with (nolock)
where id = object_id('DestTable')

-- Returns 0:
select rows
from sys.partitions
where object_id = object_id('DestTable')

Кроме того, мы можем видеть транзакцию sys.dm_tran_active_transactions, но я не смог найти способ получить количество затронутых строк в заданном transaction_id(что-то похожее на @@ROWCOUNTвозможно, но с transaction_idаргументом as).

Я понимаю, что на SQL Server оператор SELECT INTO является одновременно оператором DDL и DML, и поэтому создание неявных таблиц будет операцией блокировки. Я все еще думаю, что должен быть какой-то умный способ получить какую-то информацию о прогрессе во время выполнения оператора.

Дэн
источник
Если бы вы использовали глобальную временную таблицу ## TABLE, могли бы вы выполнить операцию Выбрать со счетчиком в столбце индекса на ## TABLE, чтобы получить количество уже записанных записей и приблизительное количество записей, которые должны быть записаны?
CoveGeek

Ответы:

6

Я подозреваю, что rowsв sys.partitionsэто 0 из-за еще не совершено. Но это не означает, что SQL Server не знает, что произойдет, если транзакция будет зафиксирована. Главное - помнить, что все операции сначала проходят через буферный пул (т. Е. Память), независимо от COMMIT или ROLLBACK операции. Следовательно, мы можем искать sys.dm_os_buffer_descriptorsэту информацию:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

SELECT  --OBJECT_NAME(sp.[object_id]) AS [TableName], sdobd.*, '---', sp.*, '---', sau.*
       SUM(sdobd.[row_count]) AS [BufferPoolRows],
       SUM(sp.[rows]) AS [AllocatedRows],
       COUNT(*) AS [DataPages]
FROM sys.dm_os_buffer_descriptors sdobd
INNER JOIN  sys.allocation_units sau
        ON sau.[allocation_unit_id] = sdobd.[allocation_unit_id]
INNER JOIN  sys.partitions sp
        ON  (   sau.[type] = 1
            AND sau.[container_id] = sp.[partition_id]) -- IN_ROW_DATA
        OR  (   sau.[type] = 2
            AND sau.[container_id] = sp.[hobt_id]) -- LOB_DATA
        OR  (   sau.[type] = 3
            AND sau.[container_id] = sp.[partition_id]) -- ROW_OVERFLOW_DATA
WHERE   sdobd.[database_id] = DB_ID()
AND     sdobd.[page_type] = N'DATA_PAGE'
AND     sp.[object_id] = (SELECT so.[object_id]
                          FROM   sys.objects so
                          WHERE  so.[name] = 'TestDump')

Если вы хотите увидеть детали, раскомментируйте первый ряд элементов в SELECTсписке, закомментируйте оставшиеся 3 строки.

Я проверил, запустив следующее в одном сеансе, а затем повторно запустил запрос выше в другом.

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
Соломон Руцкий
источник
1
Это креативно. Просто хочу добавить предупреждение, что перечисление большого пула буферов идет очень медленно.
USR
1
Предполагается, что из пула буферов еще не было удалено ни одной страницы.
Мартин Смит
@MartinSmith Могут ли страницы быть удалены до фиксации?
Соломон Руцкий,
5
@srutzky - да. Журнал транзакций содержит всю информацию, необходимую для отката. Грязные страницы могут быть записаны на диск - например, на контрольной точке или разработчиком Eager, особенно в этом случае, а затем удалены из пула буферов.
Мартин Смит
7

В целях мониторинга мы хотели бы получить приблизительное представление о ходе выполнения этого заявления, пока оно выполняется.

Один выходной или продолжающийся?

Если это необходимо, что можно ожидать заранее * вы можете использовать sys.dm_exec_query_profiles

Соединение 1 (сеанс 55)

SET STATISTICS XML ON

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.all_objects so1
CROSS JOIN sys.all_objects so2
CROSS JOIN sys.all_objects so3
CROSS JOIN sys.all_objects so4
CROSS JOIN sys.all_objects so5;

Соединение 2

select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert' 
    AND session_id = 55;

Вы , возможно , потребуется просуммировать строк счетчики возвращаемые если SELECT INTOесть используя параллелизм .

* Сеанс, который вы хотите отслеживать с помощью этого DMV, должен быть включен для сбора статистики с помощью SET STATISTICS PROFILE ONили SET STATISTICS XML ON. Запрос «фактического» плана выполнения из SSMS также работает (потому что он устанавливает последний вариант).

Мартин Смит
источник
Кажется, я забыл +1 еще в феврале, но не совсем об этом забыл :). Я только что использовал его в этом связанном Вопросе, так как этот ОП как минимум 2014 года: dba.stackexchange.com/questions/139191/… Спасибо за то, что указал на это; это довольно удобный DMV :-)
Соломон Руцкий
2
@srutzky Да, это очень полезно. И использовал в планах оперативного
Мартин Смит
5

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

SELECT writes 
  FROM sys.dm_exec_requests WHERE session_id = <x>;

SELECT COUNT(*) FROM sys.dm_db_database_page_allocations
(<dbid>, OBJECT_ID(N'dbo.newtablename'), 0, NULL, 'LIMITED');

Если у вас есть какое-то представление о том, сколько страниц должна занять куча, когда вы закончите, вы сможете уточнить% выполнения. Последний запрос не будет быстрым, поскольку таблица становится больше. И, вероятно, безопаснее всего запустить выше READ UNCOMMITTED(и это не часто, я рекомендую это, для чего-либо).

Аарон Бертран
источник
4

Если бы вы могли изменить INSERTот

SELECT ... INTO DestTable FROM SrcTable

к

INSERT DestTable SELECT ... FROM SrcTable

тогда ваш select count(*) from DestTable with (nolock)запрос будет работать.

Если это невозможно, вы можете использовать sp_WhoIsActive (или углубиться в DMV), чтобы отслеживать, сколько записей выполняет запрос. Это было бы довольно грубым показателем, но могло бы быть полезным, если бы вы указали количество записей, которые оно обычно выполняет.

Вы должны быть в состоянии получить минимальное логирование с INSERTвышеупомянутым, если вы добавите WITH (TABLOCK).

Джеймс Андерсон
источник
Спасибо за этот комментарий. Мы хотим получить минимальное ведение журнала, поэтому мы используем подход SELECT ... INTO (а также потому, что мы немного ленивые ...)
Дан
1
Вы должны быть в состоянии получить минимальное количество входов в систему, INSERTесли вы добавитеWITH(TABLOCK)
Джеймс Андерсон
@JamesAnderson - Если таблица оставлена ​​в виде кучи, это просто вызовет повторную блокировку, поскольку она принимает BULK_OPERATIONблокировку.
Мартин Смит,