Kolumna NVARCHAR jako KLUCZ PODSTAWOWY lub jako kolumna UNIKALNA


11

Tworzę bazę danych SQL Server 2012 i mam wątpliwości co do kolumn nvarchar jako kluczy podstawowych.

Mam ten stół:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Ale teraz chcę użyć [CODE]kolumny jako klucza podstawowego i usunąć [ID_CODE]kolumnę.

Czy jest jakaś kwestia lub kara, jeśli mam NVARCHARkolumnę jako PRIMARY KEY?

[CODE]wartość kolumny musi być unikalna, więc pomyślałem, że mogę ustawić UNIQUEograniczenie dla tej kolumny.

Czy muszę używać [CODE]jako klucza podstawowego, czy lepiej jest, jeśli ustawię UNIQUEograniczenie dla [CODE]kolumny?


1
Dość ważną rzeczą do rozważenia jest to, ile wierszy będzie w twoim stole?
James Z

Nie jest to odpowiedź sama w sobie , ale jestem skłonny sądzić, że twoja CODEkolumna powinna być unikalna, ale nie klucz podstawowy. Podejrzewam, że zawiera informacje. Jeśli informacje te można w jakikolwiek sposób zmienić, CODEnależy je zmienić lub być nieaktualne. To sprawiłoby, że Twój Klucz Podstawowy byłby niestabilny i nie widzę, żeby to się dobrze skończyło. Najlepiej, aby Twój PK był tylko kluczem, a Twój KOD może robić, co lubi. Po prostu opinia.
Manngo

@Manngo, dzięki za komentarz. Tak, zrobiłem w ten sposób: ID_CODE jest kluczem podstawowym, a CODE jest UNIQUE.
VansFannel,

Odpowiedzi:


13

Tak, absolutnie istnieją negatywne konsekwencje używania łańcucha zamiast typu liczbowego dla klucza podstawowego, a tym bardziej, jeśli PK jest klastrowany (co w rzeczywistości tak jest w twoim przypadku). Jednak stopień, w jakim widzisz efekt (y) użycia pola ciągu, jest funkcją a) liczby wierszy w tej tabeli oraz b) liczby wierszy w innych tabelach z kluczem obcym dla tej PK. Jeśli masz tylko 10 tys. Wierszy w tej tabeli i 100 tys. Wierszy w kilku innych tabelach, które FK do tej tabeli za pośrednictwem tego pola, być może nie będzie to tak zauważalne. Ale te efekty z pewnością stają się bardziej zauważalne wraz ze wzrostem liczby wierszy.

Należy wziąć pod uwagę, że pola w indeksie klastrowanym są przenoszone do indeksów nieklastrowanych. Więc nie patrzysz tylko na 40 bajtów na wiersz, ale (40 * jakiś_numer) bajtów. I w dowolnych tabelach FK masz te same 40 bajtów w wierszu, a częściej w tym polu będzie indeks nieklastrowany, ponieważ jest on używany w połączeniach, więc teraz jest naprawdę podwojony w tabelach, do których FK ten. Jeśli ktoś uważa, że ​​40 bajtów * 1 milion wierszy * 10 jego kopii nie ma się czym martwić, zobacz mój artykuł Dysk jest tani! ORLY? które wyszczególniają wszystkie (lub przynajmniej większość) obszary, na które wpływa ta decyzja.

Inną rzeczą do rozważenia jest to, że filtrowanie i sortowanie ciągów, szczególnie gdy nie jest używane sortowanie binarne (zakładam, że używasz domyślnej bazy danych, która zazwyczaj nie rozróżnia wielkości liter) jest znacznie mniej wydajne (tj. Trwa dłużej) niż przy użyciu INT/ BIGINT. Wpływa to na wszystkie zapytania filtrujące / łączące / sortujące w tym polu.

Dlatego użycie czegoś podobnego CHAR(5)byłoby prawdopodobnie OK dla klastrowanego PK, ale głównie, jeśli zostało to również zdefiniowane za pomocą COLLATE Latin1_General_100_BIN2(lub czegoś takiego).

I czy wartość [CODE]kiedykolwiek może się zmienić? Jeśli tak, to jeszcze więcej powodów, aby nie używać go jako PK (nawet jeśli ustawisz FK na ON UPDATE CASCADE). Jeśli to nie może lub nigdy się nie zmieni, to dobrze, ale wciąż istnieje wystarczający powód, aby nie używać go jako PK.

Oczywiście pytanie może być niepoprawnie sformułowane, ponieważ wydaje się, że masz już to pole w swoim PK.

Niezależnie od tego, jak dotąd najlepszą opcją jest użycie [ID_CODE]jako klastrowanego PK, użycie tego pola w powiązanych tabelach jako FK i zachowanie go [CODE]jako UNIQUE INDEX(co oznacza, że ​​jest to „klucz zastępczy”).


Aktualizacja
Trochę więcej informacji na podstawie tego pytania w komentarzu do tej odpowiedzi:

Czy [ID_CODE], jako PODSTAWOWY KLUCZ, jest najlepszą opcją, jeśli używam kolumny [KOD] do wyszukiwania w tabeli?

Wszystko zależy od bardzo wielu czynników, z których niektóre już wspomniałem, ale powtórzę:

Klucz podstawowy to sposób identyfikacji pojedynczego wiersza, niezależnie od tego, czy jest do niego przypisany jakikolwiek klucz obcy. Sposób, w jaki system wewnętrznie identyfikuje wiersz, jest związany, ale niekoniecznie taki sam, z tym, jak użytkownicy identyfikują się / ten wiersz. Każda kolumna NOT NULL z unikatowymi danymi może działać, ale należy wziąć pod uwagę kwestie praktyczne, zwłaszcza jeśli PK są w rzeczywistości przywołane przez dowolne FK. Na przykład identyfikatory GUID są unikalne i niektórzy ludzie naprawdę lubią ich używać z różnych powodów, ale są dość złe dla indeksów klastrowych ( NEWSEQUENTIALIDjest lepszy, ale nie idealny). Z drugiej strony, identyfikatory GUID są w porządku jako klucze alternatywne i są używane przez aplikację do wyszukiwania wiersza, ale JOIN są nadal wykonywane przy użyciu INT (lub podobnej) PK.

Do tej pory nie powiedziałeś nam, jak [CODE]pole wpasowuje się w system ze wszystkich stron, poza tym, że wspomniałeś, że tak właśnie wyglądasz wiersze, ale czy to dotyczy wszystkich zapytań czy tylko niektórych? W związku z tym:

  • Jeśli chodzi o [CODE]wartość:

    • Jak to jest generowane?
    • Czy jest to przyrostowe czy losowe?
    • Czy jest to jednakowa długość czy długość o różnej długości?
    • Jakie znaki są używane?
    • Jeśli używasz znaków alfabetycznych: czy wielkość liter ma znaczenie czy nie?
    • Czy może się kiedykolwiek zmienić po włożeniu?
  • W odniesieniu do tej tabeli:

    • Czy jakieś inne tabele FK do tego stołu? Czy te pola ( [CODE]lub [ID_CODE]) są używane w innych tabelach, nawet jeśli nie są jawnie obce?
    • Jeśli [CODE] jedyne pole służy do uzyskiwania pojedynczych wierszy, to jaki cel [ID_CODE]służy temu polu? Jeśli nie jest używany, dlaczego ma go w ogóle (co może zależeć od odpowiedzi na „Czy [CODE]pole może się kiedykolwiek zmienić?”)?
    • Ile wierszy w tej tabeli?
    • Jeśli inne tabele odwołują się do tej tabeli, ile i ile wierszy w każdym z nich?
    • Jakie są indeksy dla tej tabeli?

Tej decyzji nie można podjąć wyłącznie na pytanie „NVARCHAR tak czy nie?”. Jeszcze raz powiem, że ogólnie rzecz biorąc nie uważam tego za dobry pomysł, ale z pewnością są chwile, kiedy jest w porządku. Biorąc pod uwagę tak małą liczbę pól w tej tabeli, jest mało prawdopodobne, że istnieje więcej lub przynajmniej niewiele indeksów. Więc może być dobrze, tak czy inaczej, [CODE]jako indeks klastrowany. A jeśli żadne inne tabele nie odwołują się do tej tabeli, może być również w porządku, czyniąc ją PK. Ale jeśli inne tabele odwołują się do tej tabeli, wybrałbym [ID_CODE]pole jako PK, nawet jeśli nie jest klastrowane.


Czy anonimowy downvoter (który wydaje się, że również głosował w dół na odpowiedź @noIDonthissystem) chciałby zaproponować jakąkolwiek konstruktywną krytykę lub wskazać jakąś wadliwą logikę?
Solomon Rutzky

Dzięki za odpowiedź. Czy [ID_CODE], jak PRIMARY KEY, najlepszym rozwiązaniem, jeśli używam [CODE]kolumna patrzeć w górę tabeli?
VansFannel,

@ VansFannel proszę zobaczyć moją aktualizację. dzięki.
Solomon Rutzky

Dołączyłem do tej społeczności dba, aby głosować na tę odpowiedź.
Ahmet Arslan

6

Musisz oddzielić pojęcia:

  • klucz podstawowy to koncepcja projektowa , logiczna właściwość wpisów w tabeli. Powinien być niezmienny przez cały czas trwania wpisu w tabeli i powinien być kluczem używanym w aplikacji do odwoływania się do wpisu.

  • indeks klastrowany to koncepcja pamięci , właściwość fizyczna. Powinien być najczęstszą ścieżką dostępu do zapytań, powinien służyć jako indeks pokrywający dla większości przypadków i spełniać jak najwięcej zapytań o zakres, jak to możliwe.

Nie jest wymagany, aby klucz podstawowy był indeksem klastrowym. Możesz mieć ID_CODEjako PK i (CODE_LEVEL, CODE)jako klucz klastrowany. Albo na odwrót.

Większy klucz klastrowy ma pewne negatywne konsekwencje, ponieważ szerszy klucz oznacza mniejszą gęstość stron indeksu i większy rozmiar zużywany we wszystkich indeksach nieklastrowanych. na ten temat rozlano już tony atramentu, np. zacznij od Więcej uwag na temat klucza klastrowania - debata indeksu klastrowego trwa! .

Ale sedno sprawy polega na tym, że wybór klastrowanego klucza indeksu jest przede wszystkim kompromisem. Z jednej strony masz wymagania co do wielkości zapasów z ogólnymi reperkusje w większej wydajności (Key -> większy rozmiar -> przepustowość więcej IO i IO jest prawdopodobnie najbardziej deficytowym zasobem masz). Z drugiej strony wybranie niewłaściwego klucza klastrowego w imię oszczędności miejsca może mieć konsekwencje w zakresie wydajności zapytań, często gorsze niż problemy wynikające z szerokiego klucza.

Jeśli chodzi o wybór klucza podstawowego, nie powinno to nawet stanowić problemu: model danych, logika aplikacji powinny dyktować, co to jest klucz podstawowy.

To powiedziawszy, mój 2c: nieNVARCHAR(20) jest szeroki. Jest idealnie akceptowalnym rozmiarem klucza klastrowego, nawet dla dużego stołu.


Dzięki za odpowiedź. Czy [ID_CODE]jest PRIMARY KEYto najlepsza opcja, jeśli używam [CODE]kolumny (i być może [CODE_LEVEL]) do wyszukiwania w tabeli?
VansFannel,

@ VansFannel tylko Ty możesz na to odpowiedzieć.
Remus Rusanu,

Ale Twoim zdaniem ...
VansFannel,

2
Moim zdaniem musiałbym wziąć pod uwagę dokładny DDL całej tabeli i wszystkich indeksów, odnoszące się do niej klucze obce, szacunkową liczbę wierszy, oczekiwane obciążenie zapytaniami, oczekiwane SLA aplikacji, a nie tylko dostępny budżet na sprzęt i licencje.
Remus Rusanu,

Dzięki. Użyję [CODE]kolumnę jako klucz podstawowy.
VansFannel,

4

Nigdy nie pozwoliłbym, aby ktoś zrobił nvarchar(20)PK w mojej bazie danych. Marnujesz miejsce na dysku i pamięć podręczną. Każdy indeks w tej tabeli i wszystkie FK do niej odzwierciedlają tę szeroką wartość. Może char (20), jeśli potrafią to uzasadnić. Jakie dane próbujesz przechowywać CODE? Czy naprawdę potrzebujesz przechowywać znaki nvarchar? Staram się, aby wartości PK były „wewnętrzne”, niewidoczne dla użytkowników, i staram się zachować wartości, które są wyświetlane osobno. Wyświetlane wartości czasami wymagają zmiany, co staje się bardzo problematyczne w przypadku PK i FK.

Czy zdajesz sobie również sprawę, że „tożsamość biginta (1,1)” może zwiększyć się do 9 223 372,036,854,775,807?

[ID_CODE] [bigint] IDENTITY(1,1)

O ile nie budujesz tej bazy danych dla Google, czy normalność int identity (1,1)z limitem ponad 2 miliardów nie wystarczy?


int to 4 bajty w SQL, co daje -2,1 mld do + 2,1 mld.
datagod

@datagod, ha dzięki, tyle cyfr pomyliłem się!
brak identyfikatora w tym systemie

Dzięki za odpowiedź. Czy [ID_CODE], jak PRIMARY KEY, najlepszym rozwiązaniem, jeśli używam [CODE]kolumna patrzeć w górę tabeli? Dzięki.
VansFannel,

Byłem w tej łodzi, dopóki ktoś nie użył sekwencyjnego charakteru „int” do przewidywania danych / użytkowników w mojej bazie danych i zebrał większość wszystkiego, co miałem. Nigdy więcej. Publiczne bazy danych muszą być nieco trudniejsze do uzyskania informacji.
DaBlue

3

Nie powinno być żadnej nieodłącznej / zauważalnej kary innej niż ryzyko użycia szerokich kluczy podczas korzystania z nvarchar / varchar, jeśli nie jest się świadomym. Zwłaszcza jeśli zaczniesz łączyć je w klucze kompozytowe.

Ale w twoim przykładzie (20) długości powinieneś być w porządku i nie martwiłbym się tym zbytnio. Ponieważ jeśli KOD polega głównie na wyszukiwaniu danych - indeks klastrowany na to brzmi bardzo rozsądnie.

Należy jednak rozważyć, czy rzeczywiście jest to klucz podstawowy, czy tylko unikalny (klastrowany) indeks. Istnieje (niewielka) różnica między indeksem klastrowym a kluczem podstawowym (w zasadzie - klucz podstawowy identyfikuje dane, ale indeks polega na wyszukiwaniu danych), więc jeśli chcesz, możesz równie łatwo ustawić kod ID_Code jak klucz podstawowy i utwórz unikalny indeks klastrowy na KOD. (uwaga: SQL Server automatycznie zmieni klucz podstawowy w indeks klastrowany, chyba że samodzielnie utworzysz indeks klastrowany)

Zastanów się także, czy faktycznie potrzebujesz ID_Code, teraz masz unikalny KOD.


2
W rzeczywistości NVARCHAR(20)ma 40 bajtów (maks.), A ponieważ jest to kolumna o zmiennej długości , nie jest to naprawdę najlepszy wybór dla indeksu klastrowego. ID_CODEbycie tutaj BIGINT IDENTITYbyłoby znacznie lepszym wyborem!
marc_s

Wiem, że to 40 bajtów, ale nie było zbyt wiele powodów, aby to określać, ponieważ nie jest w pobliżu 900 bajtów. A jeśli przeszukujesz głównie dane z KODU, lepszym wyborem byłoby uniknięcie utrzymywania nadmiarowych indeksów, ponieważ nadal będziesz potrzebować indeksu, a następnie będziesz musiał przeszukać klastrowane strony
Allan S. Hansen

Warto wspomnieć - o którym zapomniałem wspomnieć i podejrzewam, że to, do czego odnosi się @marc_s, polega na tym, że indeks tego typu może prowadzić do większej fragmentacji indeksu niż tożsamość sekwencyjna, ale nadal widzę go jako sensowny indeks w tej konkretnej sytuacji na czynnik zapytania.
Allan S. Hansen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.