У меня есть большая таблица данных. В этой таблице 10 миллионов записей.
Как лучше всего выполнить этот запрос
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
У меня есть большая таблица данных. В этой таблице 10 миллионов записей.
Как лучше всего выполнить этот запрос
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Ответы:
Если вы удаляете все строки в этой таблице, самым простым вариантом является усечение таблицы, что-то вроде
TRUNCATE TABLE LargeTable
GO
Таблица Truncate просто очистит таблицу, вы не можете использовать предложение WHERE для ограничения удаляемых строк, и никакие триггеры не будут срабатывать.
С другой стороны, если вы удаляете более 80-90 процентов данных, скажем, если у вас всего 11 миллионов строк и вы хотите удалить 10 миллионов, другим способом было бы Вставить эти 1 миллион строк (записи, которые вы хотите сохранить ) в другой промежуточный стол. Обрежьте эту большую таблицу и вставьте обратно эти 1 миллион строк.
Или, если разрешения / представления или другие объекты, которые имеют эту большую таблицу в качестве базовой таблицы, не пострадают при удалении этой таблицы, вы можете получить это относительно небольшое количество строк в другой таблице, отбросьте эту таблицу и создайте другую таблицу с той же схемой и импортируйте их. строк обратно в эту бывшую большую таблицу.
Последний вариант, о котором я могу подумать, - это изменить вашу базу данных, Recovery Mode to SIMPLE
а затем удалять строки меньшими партиями, используя цикл while примерно так ...
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
и не забудьте снова изменить режим восстановления на полный, и я думаю, вам нужно сделать резервную копию, чтобы сделать ее полностью эффективной (режимы изменения или восстановления).
optimal solution for unknown case
это мечта, не так ли? К сожалению, вы не можете вылечить каждую болезнь одной таблеткой; Я предложил несколько возможных решений для разных сценариев. К сожалению, здесь нет серебряной пули.
@ m-ali ответ правильный, но имейте в виду, что журналы могут сильно вырасти, если вы не фиксируете транзакцию после каждого фрагмента и не выполняете контрольную точку. Вот как я бы сделал это и взял эту статью http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes в качестве справочной с тестами производительности и графиками:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
COMMIT TRANSACTION
и CHECKPOINT
журналы по - прежнему растет. Спасибо, что разъяснили это.
@Deleted_Rows
с 10000, или у вас может получиться бесконечный цикл из-за того, что он бесконечно удаляет небольшие наборы данных. Итак WHILE (@Deleted_Rows = 10000)
- как только не будет полной «страницы» данных для удаления, она остановится. В вашей реализации WHILE (@Deleted_Rows > 0)
цикл while будет выполняться снова, даже если он удалил только одну строку, и следующее выполнение может также найти строку или две для удаления, что приведет к бесконечному циклу.
WHILE
самого цикла: dateadd(MONTH,-7,GETDATE())
.
WHILE
цикла.
Вы также можете использовать GO +, сколько раз вы хотите выполнить один и тот же запрос.
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
должен работать этот синтаксис ? Я получаю сообщение об ошибке «Не удалось найти хранимую процедуру» . Однако без GO
команды он работает нормально.
@Francisco Goldenstein, небольшая поправка. COMMIT необходимо использовать после установки переменной, иначе WHILE будет выполнен только один раз:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
Эта вариация M.Ali мне подходит. Некоторые удаляются, журнал очищается и повторяется. Я смотрю, как журнал растет, опускается и начинается сначала.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
# of rows
удаление за раз, а также WHERE
предложение. Работает как шарм!
Если вы хотите (и можете) реализовать секционирование, это эффективный метод для удаления больших объемов данных с небольшими затратами времени выполнения. Однако это не рентабельно для разового упражнения.
Я смог удалить 19 миллионов строк из моей таблицы, состоящей из 21 миллиона строк, за считанные минуты . Вот мой подход.
Если у вас есть автоматически увеличивающийся первичный ключ в этой таблице, вы можете использовать этот первичный ключ.
Получить минимальное значение первичного ключа большой таблицы, где readTime <dateadd (MONTH, -7, GETDATE ()). (Добавьте индекс в readTime, если он еще не присутствует, этот индекс все равно будет удален вместе с таблицей на шаге 3.). Сохраним его в переменной min_primary.
Вставьте все строки с первичным ключом> min_primary в промежуточную таблицу (таблица памяти, если количество строк невелико).
Бросьте большой стол.
Восстановите таблицу. Скопируйте все строки из промежуточной таблицы в основную.
Отбросьте промежуточный стол.
Вы можете удалять небольшие партии, используя цикл while, примерно так:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
Другое использование:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
По желанию;
Если журнал транзакций включен, отключите журналы транзакций.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Если вы используете SQL Server 2016 или выше, и если в вашей таблице есть разделы, созданные на основе столбца, который вы пытаетесь удалить (например, столбец Timestamp), вы можете использовать эту новую команду для удаления данных по разделам.
ОБРЕЗАТЬ ТАБЛИЦУ С (РАЗДЕЛЫ ({|} [, ... n]))
Это приведет к удалению данных только в выбранном разделе (ах) и должно быть наиболее эффективным способом удаления данных из части таблицы, поскольку при этом не будут создаваться журналы транзакций и будет выполняться так же быстро, как и при обычном усечении, но без удаления всех данных. из таблицы.
Недостатком является то, что если ваша таблица не настроена с разделением, вам нужно пойти по старой школе и удалить данные с помощью обычного подхода, а затем воссоздать таблицу с разделами, чтобы вы могли сделать это в будущем, что я и сделал. Я добавил создание и удаление разделов в саму процедуру вставки. У меня была таблица с 500 миллионами строк, так что это был единственный способ сократить время удаления.
Для получения дополнительных сведений см. Ссылки ниже: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
Таблица усечения SQL Server 2016 с разделами
Ниже показано, что я сделал сначала, чтобы удалить данные, прежде чем смог воссоздать таблицу с разделами с необходимыми данными в ней. Этот запрос будет выполняться в течение нескольких дней в течение указанного временного окна, пока данные не будут удалены.
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()
Если я говорю без цикла, я могу использовать GOTO
оператор для удаления большого количества записей с помощью sql-сервера. экс.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
таким образом вы можете удалить большой объем данных с меньшим размером удаления.
дайте мне знать, если потребуется дополнительная информация.