Пакетная фиксация большой операции INSERT в собственном SQL?

StackOverflow https://stackoverflow.com/questions/1602244

Вопрос

У меня есть пара больших таблиц (188 и 144 млн строк), которые мне нужно заполнить из представлений, но каждое представление содержит несколько сотен миллионов строк (собирающих данные псевдоразмерной модели в плоскую форму).Ключи каждой таблицы составляют более 50 составных байтов столбцов.Если бы данные находились в таблицах, я всегда мог бы подумать об использовании sp_rename для создания другой новой таблицы, но на самом деле это не вариант.

Если я выполняю одну операцию INSERT, этот процесс использует огромный объем пространства журнала транзакций, что обычно приводит к его заполнению и вызывает кучу проблем с администраторами баз данных.(И да, вероятно, эту работу должны выполнять/проектировать/архитировать администраторы баз данных)

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

Есть ли что-нибудь, кроме разделения процесса на несколько операций INSERT с использованием какого-то ключа для распределения строк по разным пакетам и выполнения цикла?

Это было полезно?

Решение

Вы можете разделить ваши данные и вставить их в цикл курсора. Это было бы почти так же, как пакетная вставка служб SSIS. Но работает на вашем сервере.

create cursor ....
select YEAR(DateCol), MONTH(DateCol) from whatever

while ....
    insert into yourtable(...)
    select * from whatever 
    where YEAR(DateCol) = year and MONTH(DateCol) = month
end

Другие советы

Имеет ли представление ЛЮБОЙ вид уникального идентификатора / ключа-кандидата? Если это так, вы можете выбрать эти строки в рабочую таблицу, используя:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

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

Затем вы можете сделать что-то вроде этого, вставляя 10 000 строк за раз, и резервное копирование журнала между:

SET NOCOUNT ON;

DECLARE
    @batchsize INT,
    @ctr INT,
    @rc INT;

SELECT
    @batchsize = 10000,
    @ctr = 0;

WHILE 1 = 1
BEGIN
    WITH x AS
    (
        SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column)
        FROM dbo.temp
    )
    INSERT dbo.PrimaryTable(a, b, c, etc.)
        SELECT v.a, v.b, v.c, etc.
        FROM x
        INNER JOIN dbo.HugeView AS v
        ON v.key_column = x.key_column
        WHERE x.rn > @batchsize * @ctr
        AND x.rn <= @batchsize * (@ctr + 1);

    IF @@ROWCOUNT = 0
        BREAK;

    BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT;

    SET @ctr = @ctr + 1;
END

Это все, что у меня в голове, так что не надо резать / вставлять / бегать, но я думаю, что общая идея есть.

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

Я знаю, что это старый поток, но я сделал универсальную версию решения для курсора Артура:

--Split a batch up into chunks using a cursor.
--This method can be used for most any large table with some modifications
--It could also be refined further with an @Day variable (for example)

DECLARE @Year INT
DECLARE @Month INT

DECLARE BatchingCursor CURSOR FOR
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>)
FROM <Sometable>;


OPEN BatchingCursor;
FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
WHILE @@FETCH_STATUS = 0
BEGIN

--All logic goes in here
--Any select statements from <Sometable> need to be suffixed with:
--WHERE Year(<SomeDateField>)=@Year AND Month(<SomeDateField>)=@Month   


  FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
END;
CLOSE BatchingCursor;
DEALLOCATE BatchingCursor;
GO

Это решило проблему с нагрузками на наши большие таблицы.

Пикси-пыли нет, вы это знаете.

Не зная специфики фактической передаваемой схемы, универсальное решение будет именно таким, как вы его описываете: разделите обработку на несколько вставок и отслеживайте ключ (ы). Это своего рода псевдокод T-SQL:

create table currentKeys (table sysname not null primary key, key sql_variant not null);
go

declare @keysInserted table (key sql_variant);
declare @key sql_variant;
begin transaction
do while (1=1)
begin
    select @key = key from currentKeys where table = '<target>';
    insert into <target> (...)
    output inserted.key into @keysInserted (key)
    select top (<batchsize>) ... from <source>
    where key > @key
    order by key;

    if (0 = @@rowcount)
       break; 

    update currentKeys 
    set key = (select max(key) from @keysInserted)
    where table = '<target>';
    commit;
    delete from @keysInserted;
    set @key = null;
    begin transaction;
end
commit

Было бы сложнее, если вы хотите разрешить параллельные пакеты и разделить ключи.

Вы можете использовать команду BCP для загрузки данных и использовать параметр «Размер пакета».

http://msdn.microsoft.com/en-us/library/ms162802.aspx

Двухэтапный процесс

  • Данные BCP OUT из представлений в текстовые файлы
  • Данные BCP IN из текстовых файлов в таблицы с параметром размера пакета

Это похоже на работу для доброго старого BCP .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top