Исправлено 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, чем удаление данных.
Вам нужно будет удалить столько строк, сколько сможете, пока журнал транзакций не заполнится, а затем обрезайте его каждый раз. Ответ, предоставленный Станиславом Князевым, может быть изменен, чтобы сделать это, увеличив размер партии и добавив вызов для обрезания файла журнала.