У мене велика таблиця даних. У цій таблиці 10 мільйонів записів.
Який найкращий спосіб для цього запиту
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
У мене велика таблиця даних. У цій таблиці 10 мільйонів записів.
Який найкращий спосіб для цього запиту
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Відповіді:
Якщо ви видаляєте всі рядки з цієї таблиці, найпростіший варіант - урізати таблицю, щось подібне
TRUNCATE TABLE LargeTable
GO
Обрізана таблиця просто спорожнить таблицю, ви не можете використовувати пункт WHERE для обмеження видалення рядків і жодні тригери не будуть запускатися.
З іншого боку, якщо ви видаляєте більше 80-90 відсотків даних, скажіть, якщо у вас є 11 мільйонів рядків, і ви хочете видалити 10 мільйонів іншим способом, було б вставити ці 1 мільйон рядків (записи, які ви хочете зберегти ) до іншої постановочної таблиці. Скоротіть цю велику таблицю та вставте ці 1 мільйон рядків.
Або якщо на дозволи / перегляди чи інші об'єкти, на яких розміщена ця велика таблиця, як їх основна таблиця, не постраждає, відкинувши цю таблицю, ви можете отримати ці відносно невеликі кількості рядків в іншу таблицю, скинути цю таблицю та створити іншу таблицю з тією ж схемою та імпортувати ці ряди назад у цю колишню велику таблицю.
Останній варіант, про який я можу придумати, - це змінити базу даних, Recovery Mode to SIMPLE
а потім видалити рядки меншими партіями, використовуючи цикл у той час як щось подібне ..
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-delete як еталонну, з тестами на ефективність та графіками:
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
Ця варіація творів М.Алі працює для мене чудово. Він видаляє деякі, очищає журнал і повторює. Я спостерігаю, як журнал росте, опускається і починається спочатку.
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-сервер 2016 або новішої версії, і якщо у вашій таблиці є розділи, створені на основі стовпця, який ви намагаєтесь видалити (наприклад, стовпець Timestamp), ви можете використовувати цю нову команду для видалення даних розділами.
ТРУНКАЦІЙНА ТАБЛИЦА З (РОЗДІЛИ ({|} [, ... n]))
Це видалить дані лише у вибраних розділах і має бути найефективнішим способом видалення даних з частини таблиці, оскільки це не створюватиме журнали транзакцій і буде виконано так само швидко, як звичайне скорочення, але без видалення всіх даних зі столу.
Недолік - якщо ваша таблиця не налаштована з розділом, вам потрібно перейти в стару школу і видалити дані при регулярному підході, а потім відтворити таблицю з розділами, щоб ви могли це зробити в майбутньому, що я і зробив. Я додав створення і видалення розділу в саму процедуру вставки. У мене була таблиця з 500 мільйонами рядків, тому це був єдиний варіант скоротити час видалення.
Більш детальну інформацію можна знайти за посиланнями нижче: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL-сервер 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
таким чином ви можете видалити велику кількість даних із меншим розміром видалення.
дайте мені знати, якщо потрібна додаткова інформація.