Я работаю с 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 попытался вставить дубликат ключа. Для Trusted 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. Фоновый процесс запускается и в конечном итоге очищает данные. Вы можете прочитать о призрачных записях, если вам интересно.