Prowadzę ciągłą debatę z różnymi programistami w moim biurze na temat kosztu indeksu i tego, czy unikalność jest korzystna czy kosztowna (prawdopodobnie obie). Sednem problemu są nasze konkurujące zasoby.
tło
Wcześniej czytałem dyskusję, w której stwierdzono, że Unique
indeks nie jest dodatkowym kosztem do utrzymania, ponieważ Insert
operacja domyślnie sprawdza, gdzie mieści się w drzewie B, a jeśli duplikat zostanie znaleziony w nieunikalnym indeksie, dołącza unikalizator do koniec klucza, ale w przeciwnym razie wstawia bezpośrednio. W tej sekwencji zdarzeń Unique
indeks nie ma dodatkowych kosztów.
Mój współpracownik zwalcza to stwierdzenie, mówiąc, że Unique
jest egzekwowane jako druga operacja po poszukiwaniu nowej pozycji w drzewie B, a zatem jego utrzymanie jest bardziej kosztowne niż indeks nieunikalny.
W najgorszym przypadku widziałem tabele z kolumną tożsamości (z natury unikatową), która jest kluczem do klastrowania tabeli, ale jest wyraźnie określona jako nie-unikalna. Z drugiej strony najgorsza jest moja obsesja na punkcie wyjątkowości, a wszystkie indeksy są tworzone jako unikalne, a gdy nie jest możliwe zdefiniowanie wyraźnie unikalnej relacji do indeksu, dołączam PK tabeli na końcu indeksu, aby zapewnić wyjątkowość jest gwarantowana.
Często biorę udział w recenzowaniu kodu dla zespołu programistów i muszę być w stanie podać ogólne wytyczne dla nich. Tak, każdy indeks powinien zostać oceniony, ale jeśli masz pięć serwerów z tysiącami tabel i aż dwadzieścia indeksów w tabeli, musisz być w stanie zastosować kilka prostych reguł, aby zapewnić określony poziom jakości.
Pytanie
Czy wyjątkowość ma dodatkowy koszt zaplecza w Insert
porównaniu z kosztem utrzymania nieunikalnego indeksu? Po drugie, co jest złego w dodawaniu klucza podstawowego tabeli na końcu indeksu, aby zapewnić unikalność?
Przykładowa definicja tabeli
create table #test_index
(
id int not null identity(1, 1),
dt datetime not null default(current_timestamp),
val varchar(100) not null,
is_deleted bit not null default(0),
primary key nonclustered(id desc),
unique clustered(dt desc, id desc)
);
create index
[nonunique_nonclustered_example]
on #test_index
(is_deleted)
include
(val);
create unique index
[unique_nonclustered_example]
on #test_index
(is_deleted, dt desc, id desc)
include
(val);
Przykład
Przykład dodania Unique
klucza na końcu indeksu znajduje się w jednej z naszych tabel faktów. Jest Primary Key
to jest Identity
kolumna. Jednakże, Clustered Index
to zamiast kolumny schemat partycjonowania, a następnie trzech zagranicznych kluczowych wymiarów bez wyjątkowości. Wybrana wydajność na tym stole jest fatalna i często uzyskuję lepsze czasy wyszukiwania, korzystając Primary Key
z wyszukiwania kluczowego, zamiast korzystać z niego Clustered Index
. Inne tabele, które mają podobny projekt, ale mają Primary Key
dołączone na końcu, mają znacznie lepszą wydajność.
-- date_int is equivalent to convert(int, convert(varchar, current_timestamp, 112))
if not exists(select * from sys.partition_functions where [name] = N'pf_date_int')
create partition function
pf_date_int (int)
as range right for values
(19000101, 20180101, 20180401, 20180701, 20181001, 20190101, 20190401, 20190701);
go
if not exists(select * from sys.partition_schemes where [name] = N'ps_date_int')
create partition scheme
ps_date_int
as partition
pf_date_int all
to
([PRIMARY]);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.bad_fact_table'))
create table dbo.bad_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
fk_id int not null,
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_bad_fact_table] clustered (date_int, group_id, group_entity_id, fk_id)
)
on ps_date_int(date_int);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.better_fact_table'))
create table dbo.better_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_better_fact_table] clustered(date_int, group_id, group_entity_id, id)
)
on ps_date_int(date_int);
go
Case
iIf
struktury są ograniczone do 10 poziomów, sensowne jest, że istnieje również limit rozwiązywania nieunikalnych bytów. W twoim oświadczeniu brzmi to tak, jakby dotyczyło tylko przypadków, gdy klucz klastrowania nie jest unikalny. Czy jest to problem dlaNonclustered Index
czy klucz klastrowania jest,Unique
czy nie ma problemu zNonclustered
indeksami?