Окремі заяви - DML, DDL тощо - самі по собі є транзакціями. Так, так, після кожної ітерації циклу (технічно: після кожного оператора), будь-яке UPDATE
зміна цього твердження було зроблено автоматично.
Звичайно, завжди є виняток, правда? Можна ввімкнути непрямі транзакції за допомогою SET IMPLICIT_TRANSACTIONS , і в цьому випадку перший UPDATE
вислів розпочне транзакцію, яку вам доведеться COMMIT
або ROLLBACK
в кінці. Це налаштування рівня сеансу, яке за замовчуванням вимкнено у більшості випадків.
чи потрібно нам додавати явні BEGIN TRANSACTION / END TRANSACTION заяви, щоб ми могли будь-коли скасувати?
Ні. І насправді, враховуючи, що ви хочете мати змогу зупинити процес і перезапустити, додавання явної транзакції (або ввімкнення неявних транзакцій) було б поганою ідеєю, оскільки зупинення процесу може зловити його до того, як це зробити COMMIT
. У такому випадку вам потрібно буде вручну видати COMMIT
(якщо ви перебуваєте в SSMS) або якщо ви запускаєте це з завдання SQL Agent, то у вас немає такої можливості і може закінчитися транзакцією-сиротою.
Також ви можете встановити @CHUNK_SIZE
меншу кількість. Ескалація блокування зазвичай відбувається у 5000 замків, придбаних на одному об’єкті. Залежно від розміру рядків і якщо це робиться Lock Lock vs Page Locks, можливо, ви будете перевищувати цю межу. Якщо розмір рядка такий, що на кожну сторінку розміщується лише 1 або 2 рядки, то ви завжди можете це робити, навіть якщо це робить блокування сторінки.
Якщо таблиця розділена, то у вас є можливість встановити LOCK_ESCALATION
параметр (введений у SQL Server 2008) для таблиці AUTO
таким чином, щоб після ескалації він блокував лише розділ, а не всю таблицю. Або для будь-якої таблиці ви можете встановити той самий варіант DISABLE
, хоча вам слід бути дуже обережним. Докладні відомості див. У розділі ALTER TABLE .
Ось деяка документація, яка розповідає про ескалацію блокування та порогові значення: Ескалація блокування (вона говорить, що стосується "версій SQL Server 2008 R2 та новіших версій"). І ось повідомлення в блозі, яке стосується виявлення та виправлення ескалації блокування: Блокування в Microsoft SQL Server (Частина 12 - Ескалація блокування) .
Не пов’язане з точним запитанням, але пов’язане із запитом у запитанні, тут можна внести декілька вдосконалень (або, принаймні, це виглядає просто з огляду на нього):
Для вашого циклу робити WHILE (@@ROWCOUNT = @CHUNK_SIZE)
це трохи краще, оскільки якщо кількість рядків, оновлених за останню ітерацію, менша за суму, яку потрібно провести ОНОВЛЕННЯ, то роботи не залишається.
Якщо deleted
поле є BIT
типом даних, то чи не це значення визначається тим, чи deletedDate
є 2000-01-01
? Навіщо тобі обоє?
Якщо ці два поля є новими, і ви додали їх NULL
так, як це може бути операція в режимі он-лайн / не блокує, і тепер хочуть оновити їх до їх "за замовчуванням" значення, тоді це було не потрібно. Починаючи з SQL Server 2012 (лише для Enterprise Edition), додавання NOT NULL
стовпців із обмеженням DEFAULT - це не блокуючі операції, поки значення DEFAULT є постійним. Отже, якщо ви ще не використовуєте поля, просто опустіть і додайте знову як NOT NULL
і з обмеженням DEFAULT.
Якщо жоден інший процес не оновлює ці поля, поки ви робите це ОНОВЛЕННЯ, тоді буде швидше, якби ви поставили в чергу записи, які хотіли оновити, а потім просто відпрацюйте цю чергу. У поточному методі є показник продуктивності, оскільки вам потрібно кожного разу перепитувати таблицю, щоб отримати набір, який потрібно змінити. Натомість ви можете зробити наступне, що сканує таблицю лише один раз у цих двох полях, а потім видає лише дуже націлені оператори UPDATE. Також не передбачено жодного штрафу зупинити процес і запустити його пізніше, оскільки початкова сукупність черги просто знайде записи, що залишилися для оновлення.
- Створіть тимчасову таблицю (#FullSet), яка містить лише ключові поля з кластерного індексу в ній.
- Створіть другу тимчасову таблицю (#CurrentSet) тієї самої структури.
вставити в #FullSet через SELECT TOP(n) KeyField1, KeyField2 FROM [huge-table] where deleted is null or deletedDate is null;
TOP(n)
Знаходиться там з - за розміру таблиці. Маючи 100 мільйонів рядків у таблиці, вам дійсно не потрібно заповнювати таблицю черг цілим набором клавіш, особливо якщо ви плануєте так часто зупиняти процес і перезапускати його пізніше. Тож можливо встановити n
1 мільйон і нехай це закінчиться. Ви завжди можете запланувати це в роботі SQL Agent, яка виконує набір в 1 мільйон (а може бути, і менше), а потім чекає, коли наступний запланований час знову підійде. Потім ви можете запланувати запуск кожні 20 хвилин, щоб між наборами була певна кімната дихання n
, але він все одно закінчить весь процес без нагляду. Тоді просто потрібно видалити себе, коли більше нічого робити :-).
- в циклі, зробіть:
- Населяйте поточну партію через щось подібне
DELETE TOP (4995) FROM #FullSet OUTPUT Deleted.KeyField INTO #CurrentSet (KeyField);
IF (@@ROWCOUNT = 0) BREAK;
- Зробіть ОНОВЛЕННЯ, використовуючи щось на кшталт:
UPDATE ht SET ht.deleted = 0, ht.deletedDate='2000-01-01' FROM [huge-table] ht INNER JOIN #CurrentSet cs ON cs.KeyField = ht.KeyField;
- Очистити поточний набір:
TRUNCATE TABLE #CurrentSet;
- У деяких випадках це допомагає додати відфільтрований індекс, щоб допомогти тим,
SELECT
хто подається в #FullSet
таблицю темпів. Ось кілька міркувань, пов’язаних із додаванням такого індексу:
- Умови WHERE повинні відповідати умові WHERE вашого запиту, отже
WHERE deleted is null or deletedDate is null
- На початку процесу більшість рядків відповідатиме вашому умові WHERE, тому індекс не так корисний. Можливо, ви захочете зачекати, поки десь біля позначки 50% перед додаванням цього. Звичайно, наскільки це допомагає і коли найкраще додати індекс, змінюється через декілька факторів, тому це трохи спроб та помилок.
- Можливо, вам доведеться вручну ОНОВЛЮВАТИ ДЕРЖАВИ та / або ВІДНОВИТИ індекс, щоб оновити його в актуальному стані, оскільки базові дані змінюються досить часто
- Не забудьте пам’ятати, що індекс, допомагаючи
SELECT
, завдає шкоди, UPDATE
оскільки це ще один об’єкт, який необхідно оновити під час цієї операції, отже, більше вводу / виводу. Це грає як з використанням відфільтрованого індексу (який зменшується, коли ви оновлюєте рядки, оскільки менше рядків відповідає фільтру), так і трохи чекаєте, щоб додати індекс (якщо на початку це не буде корисним, то немає причин для виникнення сумнівів додатковий введення / виведення).
ОНОВЛЕННЯ: Будь ласка, дивіться мою відповідь на питання, пов’язане з цим питанням, для повного втілення запропонованого вище, включаючи механізм відстеження стану та чистого скасування: sql сервер: оновлення полів на величезній таблиці невеликими шматками: як отримати прогрес / статус?