Я не решаюсь добавить еще один ответ, поскольку их уже немало, но необходимо сделать несколько замечаний, которые либо не были сделаны, либо не были четко сформулированы.
Во- первых: Do не всегда использовать NVARCHAR
. Это очень опасный и часто дорогостоящий подход / подход. И не лучше сказать « Никогда не используйте курсоры», так как они иногда являются наиболее эффективным средством решения конкретной проблемы, и общий обходной WHILE
цикл выполнения цикла почти всегда будет медленнее, чем правильно сделанный Курсор.
Единственный раз, когда вы должны использовать термин «всегда», это когда вы советуете «всегда делать то, что лучше для ситуации». Конечно, это часто трудно определить, особенно когда мы пытаемся сбалансировать краткосрочные выгоды во времени разработки (менеджер: «нам нужна эта функция, о которой вы не знали до сих пор - неделю назад!») С давно расходы на техническое обслуживание (менеджер, который первоначально заставил команду завершить трехмесячный проект в трехнедельном спринте: «почему у нас возникают такие проблемы с производительностью? как мы могли бы сделать X без гибкости? один-два спринта, чтобы это исправить. Что мы можем сделать за неделю, чтобы вернуться к приоритетным задачам? И нам определенно нужно больше времени уделять дизайну, чтобы этого не происходило! »).
Второе: ответ @ gbn затрагивает некоторые очень важные моменты, которые следует учитывать при принятии определенных решений по моделированию данных, когда путь не ясен на 100%. Но есть еще что рассмотреть:
- размер файлов журнала транзакций
- время, необходимое для репликации (при использовании репликации)
- время, необходимое для ETL (если ETLing)
- время доставки журналов в удаленную систему и восстановления (если используется доставка журналов)
- размер резервных копий
- время, необходимое для завершения резервного копирования
- время, необходимое для восстановления (это может быть важно когда-нибудь ;-)
- размер, необходимый для tempdb
- выполнение триггеров (для вставленных и удаленных таблиц, которые хранятся в базе данных tempdb)
- производительность управления версиями строк (если используется SNAPSHOT ISOLATION, поскольку хранилище версий находится в базе данных tempdb)
- возможность получить новое дисковое пространство, когда финансовый директор говорит, что в прошлом году он только что потратил 1 миллион долларов на сеть хранения данных, и поэтому они не будут выделять еще 250 тысяч долларов на дополнительное хранилище.
- продолжительность выполнения операций INSERT и UPDATE
- время, необходимое для ведения индекса
- и т. д. и т. д.
Потеря пространства имеет огромный каскадный эффект на всю систему. Я написал статью, подробно описав эту тему: « Диск дешев! ORLY? (требуется бесплатная регистрация; извините, я не контролирую эту политику).
Третье: хотя некоторые ответы неправильно фокусируются на аспекте «это маленькое приложение», а некоторые правильно предлагают «использовать то, что уместно», ни один из ответов не дал реального руководства ОП. Важная деталь, упомянутая в Вопросе является то, что это веб-страница для их школы. Большой! Таким образом, мы можем предположить, что:
- Поля для имен студентов и / или преподавателей, вероятно, должны быть такими,
NVARCHAR
поскольку со временем становится все более вероятным, что имена из других культур будут появляться в этих местах.
- Но для адреса и названия города? Цель приложения не была указана (это было бы полезно), но предполагая, что адресные записи, если таковые имеются, относятся только к определенному географическому региону (т. Е. К одному языку / культуре), затем используйте их
VARCHAR
с соответствующей кодовой страницей (которая определяется из сопоставления поля).
- При хранении кодов ISO штата и / или страны (не нужно хранить
INT
/, TINYINT
поскольку коды ISO имеют фиксированную длину, удобочитаемы и, как правило, стандартны :), используйте CHAR(2)
двухбуквенные коды и CHAR(3)
3-буквенные коды. И рассмотрите возможность использования бинарной сортировки, такой как Latin1_General_100_BIN2
.
- При хранении почтовых индексов (т.е. почтовых индексов) используйте,
VARCHAR
поскольку это международный стандарт, никогда не используйте никакие буквы за пределами AZ. И да, по-прежнему используйте, VARCHAR
даже если хранятся только почтовые индексы США, а не INT, поскольку почтовые индексы не являются числами, они являются строками, и некоторые из них имеют начальный «0». И рассмотрите возможность использования бинарной сортировки, такой как Latin1_General_100_BIN2
.
- Если вы храните адреса электронной почты и / или URL-адреса, используйте их,
NVARCHAR
поскольку теперь они могут содержать символы Юникода.
- и так далее....
Четвертое: теперь, когда у вас есть NVARCHAR
данные, занимающие вдвое больше места, чем нужно для данных, которые хорошо вписываются VARCHAR
(«хорошо вписывается» = не превращаются в «?») И каким-то образом, как по волшебству, приложение росло и теперь есть миллионы записей, по крайней мере, в одном из этих полей, где большинство строк являются стандартными ASCII, но некоторые содержат символы Юникода, поэтому вам следует сохранить NVARCHAR
следующее:
Если вы используете SQL Server 2008 - 2016 RTM и используете Enterprise Edition, или, если используете SQL Server 2016 с пакетом обновления 1 (который сделал сжатие данных доступным во всех выпусках) или новее, вы можете включить сжатие данных . Сжатие данных может (но не будет «всегда») сжатие данные Unicode в NCHAR
и NVARCHAR
поле. Определяющими факторами являются:
NCHAR(1 - 4000)
и NVARCHAR(1 - 4000)
использовать стандартную схему сжатия для Unicode , но только начиная с SQL Server 2008 R2, и только для данных IN ROW, а не OVERFLOW! Это выглядит лучше, чем обычный алгоритм сжатия ROW / PAGE.
NVARCHAR(MAX)
и XML
(и я думаю, также VARBINARY(MAX)
, TEXT
и NTEXT
) данные, которые находятся в строке (не в строке на страницах LOB или OVERFLOW), по крайней мере, могут быть сжаты на PAGE, но не сжаты на ROW. Конечно, сжатие PAGE зависит от размера значения в строке: я проверил с помощью VARCHAR (MAX) и увидел, что 6000 строк символов / байтов не будут сжиматься, но 4000 строк символов / байтов сделали.
- Любые данные OFF ROW, LOB или OVERLOW = Нет сжатия для вас!
Если вы используете SQL Server 2005 или RTM 2008 - 2016, а не Enterprise Edition, у вас может быть два поля: одно VARCHAR
и одно NVARCHAR
. Например, предположим, что вы храните URL-адреса, которые в основном все являются базовыми символами ASCII (значения 0–127) и, следовательно, вписываются VARCHAR
, но иногда содержат символы Unicode. Ваша схема может включать следующие 3 поля:
...
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))
);
В этой модели вы только выбираете из [URL]
вычисляемого столбца. Для вставки и обновления вы определяете, какое поле использовать, видя, изменяет ли преобразование входящее значение, которое должно иметь NVARCHAR
тип:
INSERT INTO TableName (..., URLa, URLu)
VALUES (...,
IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
);
Вы можете GZIP входящие значения в, VARBINARY(MAX)
а затем распаковать на выходе:
- Для SQL Server 2005 - 2014: вы можете использовать SQLCLR. SQL # (библиотека SQLCLR, которую я написал) поставляется с Util_GZip и Util_GUnzip в бесплатной версии
- Для SQL Server 2016 и новее: вы можете использовать встроенные функции
COMPRESS
и DECOMPRESS
функции, которые также являются GZip.
Если вы используете SQL Server 2017 или новее, вы можете сделать таблицу Clustered Columnstore Index.
Хотя этот вариант пока не подходит, в SQL Server 2019 появилась встроенная поддержка UTF-8 в VARCHAR
/ CHAR
datatypes. В настоящее время в нем слишком много ошибок, чтобы его можно было использовать, но если они исправлены, то это вариант для некоторых сценариев. Пожалуйста, ознакомьтесь с моим постом « Поддержка UTF-8 в SQL Server 2019: Спаситель или Лжепророк? », Где подробно рассматривается новая функция.