Ход выполнения инструкции 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блокировку.
— Мартин Смит,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.