Исправлено SQL

У меня есть таблица в SQL Server 2005, в которой содержится около 4 миллиардов строк. Мне нужно удалить примерно 2 миллиарда из этих строк. Если я попытаюсь сделать это за одну транзакцию, журнал транзакций заполнится, и он не сработает. У меня нет лишнего места, чтобы сделать журнал транзакций больше. Я предполагаю, что наилучшим способом продвижения пакета является удаление операторов (в партиях ~ 10000?).

Возможно, я сделаю это с помощью курсора, но это стандартный/простой/умный способ сделать это?

P.S. В этой таблице нет столбца идентификации как ПК. PK состоит из целочисленного внешнего ключа и даты.

Ответ 1

Вы можете "обрезать" удаление, что также означает, что вы не вызываете массивную нагрузку на базу данных. Если ваши резервные копии t-журнала выполняются каждые 10 минут, вы должны быть в порядке, чтобы запускать это один или два раза за тот же интервал. Вы можете запланировать его как работу агента SQL.

попробуйте что-то вроде этого:

DECLARE @count int
SET @count = 10000

    DELETE  FROM table1 
    WHERE table1id IN (
        SELECT TOP (@count) tableid
        FROM table1
        WHERE x='y'
    )

Ответ 2

Что отличает строки, которые вы хотите удалить, от тех, которые вы хотите сохранить? Будет ли это работать для вас:

while exists (select 1 from your_table where <your_condition>)
delete top(10000) from your_table
where <your_condition>

Ответ 3

Похоже, это одноразовая операция (надеюсь, для вас), и вам не нужно возвращаться к состоянию, которое на полпути это удаленное удаление - если это так, почему бы вам просто не переключиться в режим SIMPLE transaction перед запуском, а затем вернитесь к ПОЛНОМ, когда закончите?

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

вы можете сделать это в своем script с помощью smt like:

ALTER DATABASE myDB SET RECOVERY FULL/SIMPLE

Альтернативно вы можете настроить задание для сокращения журнала транзакций каждый заданный интервал времени - пока выполняется удаление. Это нехорошо, но я считаю, что это будет трюк.

Ответ 4

Хорошо, если вы использовали SQL Server Partitioning, скажем, на основе столбца даты, вы бы, возможно, отключили разделы, которые больше не требуются. Возможно, будет рассмотрено будущее исполнение.

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

Вы также можете рассмотреть следующий метод:

  • Скопировать данные во временную таблицу
  • Усечь исходную таблицу, чтобы очистить все данные
  • Переместить все из временной таблицы обратно в исходную таблицу

Ваши индексы также будут перестроены по мере добавления данных в исходную таблицу.

Ответ 5

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

Просто мои два p'enneth.

Ответ 6

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

  • Добавьте критерии, соответствующие первому столбцу в кластерном индексе, в дополнение к другим критериям
  • Отбросьте любые индексы из таблицы, а затем верните их после удаления, если это возможно, и не будет мешать чему-либо еще, что происходит в БД, но ХРАНИТЕ кластеризованный индекс

В первом пункте выше, например, если ваш ПК кластер, то найдите диапазон, который приблизительно соответствует количеству строк, которые вы хотите удалить каждую партию, и используйте это:

DECLARE @max_id INT, @start_id INT, @end_id INT, @interval INT
SELECT @start_id = MIN(id), @max_id = MAX(id) FROM My_Table
SET @interval = 100000  -- You need to determine the right number here
SET @end_id = @start_id + @interval

WHILE (@start_id <= @max_id)
BEGIN
     DELETE FROM My_Table WHERE id BETWEEN @start_id AND @end_id AND <your criteria>

     SET @start_id = @end_id + 1
     SET @end_id = @end_id + @interval
END

Ответ 7

Я согласен с тем, кто хочет, чтобы вы пересекали меньший набор записей, это будет быстрее, чем попытка выполнить всю операцию за один шаг. Вы можете столкнуться с количеством записей, которые вы должны включить в цикл. Кажется, что около 2000 в то время было приятным пятном в большинстве таблиц. Я делаю большие дельта из althouhg, некоторые из них нуждаются в меньших количествах, таких как 500. В зависимости от количества клавиш, размера записи, триггеров и т.д., Так что это действительно займет некоторые экспериментируют, чтобы найти то, что вам нужно. Это также зависит от того, насколько тяжелым является использование таблицы. Таблице с большим доступом потребуется каждая итерация цикла для более короткого времени. Если вы можете работать в нерабочее время или лучше всего в однопользовательском режиме, вы можете удалить больше записей в одном цикле.

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

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

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

Ответ 8

Вот мой пример:

-- configure script
-- Script limits - transaction per commit (default 10,000)
-- And time to allow script to run (in seconds, default 2 hours)
--
DECLARE @MAX INT
DECLARE @MAXT INT
--
-- These 4 variables are substituted by shell script.
--
SET @MAX = $MAX
SET @MAXT = $MAXT
SET @TABLE = $TABLE
SET @WHERE = $WHERE

-- step 1 - Main loop
DECLARE @continue INT
-- deleted in one transaction
DECLARE @deleted INT
-- deleted total in script
DECLARE @total INT
SET @total = 0
DECLARE @max_id INT, @start_id INT, @end_id INT, @interval INT
SET @interval = @MAX
SELECT @start_id = MIN(id), @max_id = MAX(id) from @TABLE
SET @end_id = @start_id + @interval

-- timing
DECLARE @start DATETIME
DECLARE @now DATETIME
DECLARE @timee INT
SET @start = GETDATE()
-- 
SET @continue = 1
IF OBJECT_ID (N'EntryID', 'U') IS NULL 
BEGIN
    CREATE TABLE EntryID (startid INT)
    INSERT INTO EntryID(startid) VALUES(@start_id)
END
    ELSE
BEGIN
    SELECT @start_id = startid FROM EntryID
END


WHILE (@continue = 1 AND @start_id <= @max_id)
BEGIN

    PRINT 'Start issued:   ' + CONVERT(varchar(19), GETDATE(), 120)
    BEGIN TRANSACTION
        DELETE 
        FROM @TABLE
        WHERE id BETWEEN @start_id AND @end_id AND @WHERE
        SET @deleted = @@ROWCOUNT
    UPDATE EntryID SET EntryID.startid = @end_id + 1
    COMMIT
    PRINT 'Deleted issued: ' + STR(@deleted) + ' records. ' + CONVERT(varchar(19), GETDATE(), 120) 
    SET @total = @total + @deleted
    SET @start_id = @end_id + 1
    SET @end_id = @end_id + @interval
    IF @end_id > @max_id
        SET @end_id = @max_id

    SET @now = GETDATE()
    SET @timee = DATEDIFF (second, @start, @now)
    if @timee > @MAXT
    BEGIN
    PRINT 'Time limit exceeded for the script, exiting'
    SET @continue = 0
    END
--    ELSE
--    BEGIN
--      SELECT @total 'Removed now', @timee 'Total time, seconds'   
--    END
END

SELECT @total 'Removed records', @timee 'Total time sec' , @start_id 'Next id', @max_id 'Max id', @continue 'COMPLETED? '
SELECT * from EntryID next_start_id

GO

Ответ 9

Короткий ответ: вы не можете удалить 2 миллиарда строк без каких-либо серьезных простоя базы данных.

Лучше всего скопировать данные в временную таблицу и обрезать исходную таблицу, но это заполнит ваш tempDB и будет использовать не менее logging, чем удаление данных.

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