Методи прискорення величезного ВИДАЛЕННЯ ВІД <таблиця> без застережень


37

Використання SQL Server 2005.

Я виконую величезний УВІДКЛЮЧИТИ З пунктів, де немає. Це в основному еквівалентно оператору TRUNCATE TABLE - за винятком того, що мені заборонено використовувати TRUNCATE. Проблема в тому, що таблиця величезна - 10 мільйонів рядків, і на її завершення потрібно більше години. Чи є спосіб зробити це швидше без:

  • Використання обрізання
  • Вимкнення чи випадання індексів?

T-журнал вже є на окремому диску.

Будь-які пропозиції ласкаво просимо!


2
Якщо ви будете цим займатися багато, подумайте про розділення таблиці
Гай

1
Ви не можете використовувати TRUNCATE, оскільки існують обмеження FK, що посилаються на таблицю?
Nick Chammas

Відповіді:


39

Що ви можете зробити, це пакетне видалення, як це:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable

Де ххх, скажімо, 50000

Модифікація цього, якщо ви хочете видалити дуже високий відсоток рядків ...

SELECT col1, col2, ... INTO #Holdingtable
           FROM MyTable WHERE ..some condition..

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable WHERE ...

INSERT MyTable (col1, col2, ...)
           SELECT col1, col2, ... FROM #Holdingtable

3
@tuseau: для кожного видалення потрібен простір журналу в разі помилки для відкату. Видалення рядків 50k займає менше ресурсів / місця, ніж видалення 10м рядків. Звичайно, резервні копії журналу все ще працюють і т. Д. І займають місце, але на сервері простіше багато маленьких партій, ніж відключення великих.
gbn

1
Дякую, пакетне видалення допомагає трохи, я думаю, це найкращий варіант.
тузо

2
@Phil Helmer: якщо пакетне видалення відбувається в транзакції, виграш не використовує. Інакше кожен запис у журнал менший, що просто простіше
завантаження

1
Ще один коментар: пакетне видалення допомагає надзвичайно, і видаляє 20 мільйонів рядків вниз з 1 години 42 хв до 3 хв., Але переконайтесь, що таблиця має кластерний індекс! Якщо це купа, пункт TOP створює свого роду план виконання, який заперечує будь-яке вдосконалення. Здається, згодом очевидно.
тузо

2
@Noumenon: Це гарантує, що @@ ROWCOUNT дорівнює 1
gbn

21

Ви можете використовувати пункт TOP, щоб зробити це легко:

WHILE (1=1)
BEGIN
    DELETE TOP(1000) FROM table
    IF @@ROWCOUNT < 1 BREAK
END

Фігурні дужки відформатують ваш код
gbn

@gbn Це так. ось це ще 101 010.
bernd_k

7

Я погоджуюся з пропозиціями зібрати ваші делети в керовані шматки, якщо ви не в змозі використовувати TRUNCATE, і мені подобається пропозиція падіння / створення для його оригінальності, але мені цікаво наступний коментар у вашому запитанні:

Це в основному еквівалентно оператору TRUNCATE TABLE - за винятком того, що мені заборонено використовувати TRUNCATE

Я здогадуюсь, що причина цього обмеження пов’язана із безпекою, яку потрібно надати для прямого врізання таблиці, і з тим, що вона дозволить вам усікати таблиці, окрім тієї, про яку ви маєте справу.

Якщо припустити, що це так, мені цікаво, якщо створення збереженої процедури, яка використовує TRUNCATE TABLE і використовує "EXECUTE AS", вважатиметься життєздатною альтернативою наданню прав на безпеку, необхідних для усікання таблиці безпосередньо.

Сподіваємось, це забезпечить вам необхідну швидкість, а також вирішить проблеми безпеки, які може мати ваша компанія, додавши ваш обліковий запис до ролі db_ddladmin.

Ще одна перевага використання збереженої процедури таким чином полягає в тому, що саму збережену процедуру можна заблокувати, так що використовувати її можуть лише конкретні акаунти.

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

Сподіваюся, це допомагає!


5

За винятком усікання .. Вам може допомогти лише видалення партіями.

Ви можете скинути таблицю і відтворити її, з усіма обмеженнями та покажчиками, поза курсом. У студії управління у вас є можливість скриптувати таблицю, яку потрібно скинути і створити, тому це має бути тривіальний варіант. Але це лише в тому випадку, якщо вам дозволено робити дії DDL, і я вважаю, що це насправді не варіант.


Оскільки програма призначена для одночасних операцій, зміна структури (DDL) та використання скорочення не є варіантами ... Я думаю, що пакетне видалення найкраще доступне. Спасибі, хоча.
тузо

1

Оскільки це питання є настільки важливим посиланням, я публікую цей код, який дійсно допоміг мені зрозуміти видалення за допомогою циклів, а також обмін повідомленнями в циклі для відстеження прогресу.

Запит модифікований із цього дублюючого запитання. Кредит @RLF на базі запиту.

CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects;  -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME 
DECLARE @currenttime DATETIME 

SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0  -- Total rows deleted so far is 0
SET @HowMany = 5;     -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()

WHILE @RowsTouched > 0
BEGIN
   DELETE TOP (@HowMany)
   FROM #DelTest 
   WHERE name LIKE '%sys%';

   SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
   SET @TotalRowCount = @TotalRowCount+@RowsTouched; -- Increment Total rows deleted count
   SET @currenttime = GETDATE();
   SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
   RAISERROR(@msg, 0, 1) WITH NOWAIT;  -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.  

END; 
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.