Я працюю з SQL Server та Oracle. Напевно, є деякі винятки, але для цих платформ загальною відповіддю є те, що дані та індекси будуть оновлюватися одночасно.
Я думаю, що було б корисно провести розмежування, коли індекси оновлюються для сеансу, якому належить транзакція, та для інших сеансів. За замовчуванням інші сеанси не побачать оновлені індекси, поки транзакція не буде здійснена. Однак сеанс, який належить транзакції, одразу побачить оновлені індекси.
Один із способів подумати над цим, розгляньте за столом з первинним ключем. У SQL Server та Oracle це реалізується як індекс. Більшу частину часу ми хочемо, щоб тут негайно з’явилася помилка, якщо INSERT
буде зроблено таке, що порушить первинний ключ. Щоб це сталося, індекс необхідно оновлювати одночасно з даними. Зауважте, що інші платформи, такі як Postgres, допускають відкладені обмеження, які перевіряються лише тоді, коли транзакція здійснена.
Ось швидкий демонстратор Oracle, який показує звичайний випадок:
CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));
INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit
Друге INSERT
твердження видає помилку:
Помилка SQL: ORA-00001: унікальне обмеження (XXXXXX.SYS_C00384850) порушено
00001.
00000 - "унікальне обмеження (% s.% S) порушено"
* Причина: Операція UPDATE або INSERT намагалася вставити повторюваний ключ. Для довіреного Oracle, налаштованого в режимі СУБД MAC, ви можете побачити це повідомлення, якщо повторювана запис існує на іншому рівні.
* Дія: або видаліть унікальне обмеження, або не вставляйте ключ.
Якщо ви віддаєте перевагу, щоб побачити дію оновлення індексу нижче - це проста демонстрація в SQL Server. Спочатку створіть таблицю з двома стовпцями з мільйоном рядків та некластеризованим індексом на VAL
стовпці:
DROP TABLE IF EXISTS X_TABLE_IX;
CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);
CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);
-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);
Наступний запит може використовувати некластеризований індекс, оскільки індекс є покривним індексом для цього запиту. Він містить усі дані, необхідні для його виконання. Як очікувалося, повернення не повертаються.
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Тепер розпочнемо транзакцію та оновимо VAL
майже для всіх рядків таблиці:
BEGIN TRANSACTION
UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;
Ось частина плану запитів щодо цього:
Червоним кольором є оновлення некластеризованого індексу. Синім колом є оновлення кластерного індексу, що по суті є даними таблиці. Незважаючи на те, що транзакція не була здійснена, ми бачимо, що дані та індекс оновлюються в частині виконання запиту. Зауважте, що ви не завжди будете бачити це в плані залежно від розміру залучених даних разом із можливо іншими чинниками.
Оскільки транзакція все ще не вчинена, давайте переглянемо SELECT
запит зверху.
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Оптимізатор запитів все ще може використовувати індекс, і цього разу він оцінює, що 999999 рядків буде повернуто. Виконання запиту повертає очікуваний результат.
Це була проста демонстрація, але, сподіваємось, вона трохи прояснила речі.
Як осторонь, мені відомо кілька випадків, коли можна стверджувати, що індекс не оновлюється відразу. Це робиться з міркувань продуктивності, і кінцевий користувач не повинен бачити непослідовних даних. Наприклад, іноді видалення не буде повністю застосовано до індексу в SQL Server. Запускається фоновий процес і з часом очищає дані. Ви можете прочитати про записи про привидів, якщо вам цікаво.