Хід оператора 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аргументом як).

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


Якщо ви використовували глобальну таблицю темпів ## ТАБЛИЦЯ, чи можете ви виконати Вибір із підрахунком у стовпці індексу на ## ТАБЛИЦІ, щоб отримати кількість записаних уже записів і приблизно наблизити кількість записів?
CoveGeek

Відповіді:


6

Я підозрюю, що rowsв sys.partitions0 до того, що ще не було вчинено. Але це не означає, що 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 - так. У журналі транзакцій є вся інформація, необхідна для відкату. Брудні сторінки можуть бути записані на диск - наприклад, на контрольно-пропускній точці або письменником, що прагне, особливо в цьому випадку потім видаляються з буферного пулу.
Мартін Сміт

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 так, це дуже корисно. І використовував у планах виконання в реальному часі SSMS
Martin Smith

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з a

SELECT ... INTO DestTable FROM SrcTable

до а

INSERT DestTable SELECT ... FROM SrcTable

тоді ваш select count(*) from DestTable with (nolock)запит спрацював би.

Якщо це неможливо, ви можете використовувати sp_WhoIsActive (або заглибитись у DMV), щоб контролювати, скільки запитів виконує запит. Це було б досить грубою мірою, але може бути корисною, якщо ви базуєте кількість записів, які вона зазвичай робить.

Якщо ви додасте, ви повинні мати можливість отримати мінімальний журнал із зазначеним INSERTвище WITH (TABLOCK).


Дякую за цей коментар. Ми хочемо , щоб отримати мінімальну протоколювання, тому ми використовуємо SELECT ... INTO підходу (а також тому , що ми начебто ледачі ...)
Dan

1
INSERTЯкщо ви додастеWITH(TABLOCK)
Джеймс Андерсон,

@JamesAnderson - Якщо таблицю залишити як купу, це просто спричинить повторне блокування, оскільки буде зроблено BULK_OPERATIONблокування.
Мартін Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.