Waham się, aby dodać tutaj kolejną odpowiedź, ponieważ jest już ich sporo, ale należy podać kilka kwestii, które albo nie zostały przedstawione, albo nie zostały jasno określone.
Po pierwsze: Czy nie zawsze używać NVARCHAR
. Jest to bardzo niebezpieczne i często kosztowne podejście / podejście. I nie jest lepiej powiedzieć „ Nigdy nie używaj kursorów”, ponieważ czasami są one najskuteczniejszym sposobem rozwiązania określonego problemu, a wspólne obejście wykonywania WHILE
pętli prawie zawsze będzie wolniejsze niż poprawnie wykonany Kursor.
Termin „zawsze” powinien być używany tylko wtedy, gdy zaleca się „zawsze robić to, co jest najlepsze w danej sytuacji”. To prawda, że często jest to trudne do ustalenia, szczególnie gdy próbuje się zrównoważyć krótkoterminowe zyski z czasu programowania (kierownik: „potrzebujemy tej funkcji - o której jeszcze nie wiedzieliście - tydzień temu!”) Z długim -terminowe koszty utrzymania (kierownik, który początkowo naciskał na zespół, aby ukończył 3-miesięczny projekt w 3-tygodniowym sprincie: „dlaczego mamy te problemy z wydajnością? Jak moglibyśmy zrobić X, który nie ma elastyczności? Nie możemy sobie pozwolić sprint lub dwa, aby to naprawić. Co możemy zrobić w ciągu tygodnia, abyśmy mogli wrócić do naszych priorytetowych przedmiotów? I zdecydowanie musimy spędzić więcej czasu na projektowaniu, aby tak się nie stało! ”).
Po drugie: odpowiedź @ gbn dotyczy kilku bardzo ważnych punktów, które należy wziąć pod uwagę przy podejmowaniu pewnych decyzji dotyczących modelowania danych, gdy ścieżka nie jest w 100% jasna. Ale jest jeszcze więcej do rozważenia:
- rozmiar plików dziennika transakcji
- czas potrzebny do replikacji (jeśli używasz replikacji)
- czas potrzebny na ETL (jeśli ETLing)
- czas potrzebny na wysłanie logów do zdalnego systemu i przywrócenie (jeśli używasz Log Log)
- rozmiar kopii zapasowych
- czas potrzebny na wykonanie kopii zapasowej
- czas potrzebny na przywrócenie (może to być kiedyś ważne ;-)
- rozmiar potrzebny do tempdb
- wydajność wyzwalaczy (dla wstawionych i usuniętych tabel przechowywanych w tempdb)
- wydajność wersjonowania wierszy (jeśli używasz SNAPSHOT ISOLATION, ponieważ magazyn wersji jest w tempdb)
- możliwość uzyskania nowego miejsca na dysku, gdy dyrektor finansowy poinformuje, że w zeszłym roku wydał 1 milion USD na SAN, więc nie autoryzuje kolejnych 250 000 USD na dodatkowe miejsce do przechowywania
- czas potrzebny na wykonanie operacji INSERT i UPDATE
- czas potrzebny na utrzymanie indeksu
- itp. itp. itd.
Marnowanie przestrzeni ma ogromny efekt kaskadowy na cały system. Napisałem artykuł szczegółowo opisujący ten temat: Dysk jest tani! ORLY? (wymagana darmowa rejestracja; przepraszam, nie kontroluję tych zasad).
Po trzecie: chociaż niektóre odpowiedzi nieprawidłowo koncentrują się na aspekcie „jest to mała aplikacja”, a niektóre prawidłowo sugerują „wykorzystanie tego, co jest właściwe”, żadna z odpowiedzi nie przedstawiła rzeczywistych wskazówek dla PO Ważny szczegół wymieniony w pytaniu jest to, że jest to strona internetowa ich szkoły. Wspaniały! Możemy więc zasugerować, że:
- Pola dla nazw studentów i / lub wydziałów powinny być prawdopodobnie,
NVARCHAR
ponieważ z czasem coraz bardziej prawdopodobne staje się pojawienie się nazwisk z innych kultur w tych miejscach.
- Ale dla adresu i nazwy miasta? Cel aplikacji nie został określony (byłoby to pomocne), ale zakładając, że dane adresowe, jeśli takie istnieją, odnoszą się tylko do określonego regionu geograficznego (tj. Jednego języka / kultury), należy użyć
VARCHAR
odpowiedniej strony kodowej (która jest określany na podstawie sortowania pola).
- Jeśli przechowujesz kody ISO państwa i / lub kraju (nie musisz przechowywać
INT
/ TINYINT
ponieważ kody ISO mają ustaloną długość, są czytelne dla człowieka i no cóż, standardowe :) użyj CHAR(2)
do kodów dwuliterowych i CHAR(3)
jeśli używasz kodów 3-literowych. I rozważ użycie binarnego sortowania, takiego jak Latin1_General_100_BIN2
.
- Jeśli przechowujesz kody pocztowe (tj. Kody pocztowe), używaj,
VARCHAR
ponieważ jest to międzynarodowy standard, aby nigdy nie używać żadnej litery poza AZ. I tak, nadal używaj, VARCHAR
nawet jeśli przechowujesz tylko amerykańskie kody pocztowe, a nie INT, ponieważ kody pocztowe nie są liczbami, są łańcuchami, a niektóre z nich mają wiodące „0”. I rozważ użycie binarnego sortowania, takiego jak Latin1_General_100_BIN2
.
- Jeśli przechowujesz adresy e-mail i / lub adresy URL, użyj,
NVARCHAR
ponieważ oba mogą teraz zawierać znaki Unicode.
- i tak dalej....
Po czwarte: teraz, gdy NVARCHAR
dane zajmują dwa razy więcej miejsca niż potrzeba dla danych, które ładnie się pasują VARCHAR
(„ładnie pasuje” = nie zamienia się w „?”) I jakoś, jakby magicznie, aplikacja się rozrosła a teraz są miliony rekordów w co najmniej jednym z tych pól, w których większość wierszy jest standardowym kodem ASCII, ale niektóre zawierają znaki Unicode, więc musisz je zachować NVARCHAR
, rozważ następujące kwestie:
Jeśli używasz programu SQL Server 2008 - 2016 RTM i korzystasz z wersji Enterprise Edition, LUB jeśli korzystasz z programu SQL Server 2016 z dodatkiem SP1 (który udostępnił kompresję danych we wszystkich edycjach) lub nowszym, możesz włączyć kompresję danych . Kompresja danych może (ale nie zawsze) kompresuje dane NCHAR
i NVARCHAR
pola Unicode . Czynnikami determinującymi są:
NCHAR(1 - 4000)
i NVARCHAR(1 - 4000)
użyj standardowego schematu kompresji dla Unicode , ale tylko w SQL Server 2008 R2, ORAZ tylko dla danych IN ROW, a nie PRZEKROCZENIA! To wydaje się być lepsze niż zwykły algorytm kompresji ROW / PAGE.
NVARCHAR(MAX)
i XML
(i chyba również VARBINARY(MAX)
, TEXT
i NTEXT
) dane, które są w rzędzie (nie od wiersza LOB lub przelewowe strony) mogą być co najmniej PAGE sprężonego ale nie Rów ściśnięta. Oczywiście kompresja PAGE zależy od wielkości wartości w wierszu: przetestowałem za pomocą VARCHAR (MAX) i zobaczyłem, że wiersze 6000 znaków / bajtów nie ulegają kompresji, ale wiersze 4000 znaków / bajtów tak.
- Wszelkie dane OFF ROW, LOB lub OVERLOW = Brak kompresji dla Ciebie!
Jeśli używasz programu SQL Server 2005 lub 2008-2016 RTM, a nie wersji Enterprise Edition, możesz mieć dwa pola: jedno VARCHAR
i jedno NVARCHAR
. Załóżmy na przykład, że przechowujesz adresy URL, które w większości są podstawowymi znakami ASCII (wartości 0–127), a zatem pasują do VARCHAR
, ale czasami mają znaki Unicode. Twój schemat może zawierać następujące 3 pola:
...
URLa VARCHAR(2048) NULL,
URLu NVARCHAR(2048) NULL,
URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
AND ([URLa] IS NULL OR [URLu] IS NULL))
);
W tym modelu WYBIERASZ tylko z [URL]
kolumny obliczeniowej. Do wstawiania i aktualizowania decydujesz, którego pola użyć, sprawdzając, czy konwersja zmienia wartość wejściową, która musi być NVARCHAR
typu:
INSERT INTO TableName (..., URLa, URLu)
VALUES (...,
IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
);
Możesz GZIP przychodzące wartości do, VARBINARY(MAX)
a następnie rozpakuj po wyjściu:
- W przypadku SQL Server 2005 - 2014: możesz użyć SQLCLR. SQL # (biblioteka SQLCLR, którą napisałem) zawiera Util_GZip i Util_GUnzip w wersji darmowej
- W przypadku programu SQL Server 2016 i nowszych: można korzystać z wbudowanych funkcji
COMPRESS
i DECOMPRESS
funkcji, które są także GZip.
Jeśli korzystasz z programu SQL Server 2017 lub nowszego, możesz sprawdzić, czy tabela stanie się Clustered Columnstore Index.
Chociaż nie jest to jeszcze opłacalna opcja, SQL Server 2019 wprowadza natywną obsługę UTF-8 w VARCHAR
/ CHAR
typy danych. Obecnie jest z nim zbyt wiele błędów, aby można go było użyć, ale jeśli zostaną naprawione, jest to opcja w niektórych scenariuszach. Zapoznaj się z moim postem „ Natywna obsługa UTF-8 w programie SQL Server 2019: Zbawiciel czy fałszywy prorok? ”, Aby uzyskać szczegółową analizę tej nowej funkcji.