Pracuję z SQL Server i Oracle. Prawdopodobnie istnieją pewne wyjątki, ale w przypadku tych platform ogólną odpowiedzią jest to, że dane i indeksy będą aktualizowane w tym samym czasie.
Myślę, że warto byłoby rozróżnić, kiedy indeksy są aktualizowane dla sesji będącej właścicielem transakcji i dla innych sesji. Domyślnie inne sesje nie zobaczą zaktualizowanych indeksów, dopóki transakcja nie zostanie zatwierdzona. Jednak sesja będąca właścicielem transakcji natychmiast zobaczy zaktualizowane indeksy.
Aby zastanowić się nad tym sposobem, zastanów się przy stole z kluczem podstawowym. W SQL Server i Oracle jest to zaimplementowane jako indeks. Przez większość czasu chcemy, aby natychmiast wystąpił błąd, jeśli INSERT
zostanie wykonane, co naruszałoby klucz podstawowy. Aby tak się stało, indeks musi zostać zaktualizowany w tym samym czasie co dane. Należy pamiętać, że inne platformy, takie jak Postgres, zezwalają na odroczone ograniczenia, które są sprawdzane tylko w momencie zatwierdzenia transakcji.
Oto szybkie demo Oracle pokazujące typowy przypadek:
CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));
INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit
Druga INSERT
instrukcja zgłasza błąd:
Błąd SQL: ORA-00001: Naruszono unikalne ograniczenie (XXXXXX.SYS_C00384850)
00001.
00000 - „Naruszono wyjątkowe ograniczenie (% s.% S)”
* Przyczyna: Instrukcja UPDATE lub INSERT próbowała wstawić duplikat klucza. W przypadku Trusted Oracle skonfigurowanego w trybie DBMS MAC może zostać wyświetlony ten komunikat, jeśli na innym poziomie istnieje zduplikowany wpis.
* Działanie: Usuń unikalne ograniczenie lub nie wkładaj klucza.
Jeśli wolisz, aby akcja aktualizacji indeksu poniżej była prostą wersją demonstracyjną w SQL Server. Najpierw utwórz tabelę z dwiema kolumnami z milionem wierszy i indeks nieklastrowany w VAL
kolumnie:
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);
Poniższe zapytanie może korzystać z indeksu nieklastrowanego, ponieważ indeks jest indeksem obejmującym to zapytanie. Zawiera wszystkie dane potrzebne do jego wykonania. Zgodnie z oczekiwaniami zwroty nie są zwracane.
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Teraz zacznijmy transakcję i zaktualizuj VAL
prawie wszystkie wiersze w tabeli:
BEGIN TRANSACTION
UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;
Oto część planu zapytań:
Zakreślona na czerwono aktualizacja indeksu nieklastrowanego. Zakreślona na niebiesko aktualizacja indeksu klastrowego, który jest zasadniczo danymi tabeli. Mimo że transakcja nie została zatwierdzona, widzimy, że dane i indeks są aktualizowane w ramach wykonywania zapytania. Pamiętaj, że nie zawsze zobaczysz to w planie, w zależności od wielkości danych i ewentualnie innych czynników.
Ponieważ transakcja nadal nie została zatwierdzona, wróćmy do SELECT
zapytania z góry.
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Optymalizator zapytań nadal może korzystać z indeksu i tym razem szacuje, że zostanie zwróconych 999999 wierszy. Wykonanie zapytania zwraca oczekiwany wynik.
To było proste demo, ale mam nadzieję, że trochę to wyjaśniło.
Nawiasem mówiąc, zdaję sobie sprawę z kilku przypadków, w których można argumentować, że indeks nie jest natychmiast aktualizowany. Odbywa się to ze względu na wydajność, a użytkownik końcowy nie powinien widzieć niespójnych danych. Na przykład, czasami usunięcia nie zostaną w pełni zastosowane do indeksu w SQL Server. Proces w tle jest uruchamiany i ostatecznie czyści dane. Jeśli jesteś ciekawy, możesz przeczytać o zapisach o duchach .