У мене велика таблиця даних. У цій таблиці 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
таким чином ви можете видалити велику кількість даних із меншим розміром видалення.
дайте мені знати, якщо потрібна додаткова інформація.