Каковы основные различия в производительности между типами данных varchar и nvarchar SQL Server?

Я работаю над базой данных для небольшого веб-приложения в своей школе, используя SQL Server 2005.
Я вижу пару школ мысли по вопросу varchar vs nvarchar:

  • Используйте varchar, если вы не имеете дело с большим количеством интернационализированных данных, затем используйте nvarchar.
  • Просто используйте nvarchar для всего.

Я начинаю видеть достоинства взгляда 2. Я знаю, что nvarchar занимает в два раза больше места, но это не обязательно огромная сделка, так как это будет собирать данные только для нескольких сотен студентов. Мне кажется, что было бы проще не беспокоиться об этом и просто позволить всем использовать nvarchar. Или что-то мне не хватает?

Ответ 1

Всегда используйте nvarchar.

Вам может не понадобиться двухбайтовые символы для большинства приложений. Однако, если вам нужно поддерживать двухбайтовые языки, и у вас есть только однобайтная поддержка в вашей схеме базы данных, очень дорого вернуться и изменить во всем приложении.

Стоимость переноса одного приложения из varchar в nvarchar будет намного больше, чем немного дополнительного дискового пространства, которое вы будете использовать в большинстве приложений.

Ответ 2

Дисковое пространство не является проблемой... но память и производительность будут. Двойной просмотр страницы, двойной размер индекса, странный LIKE и = постоянное поведение и т.д.

Вам нужно хранить китайский и т.д. script? Да или нет...

И из MS BOL " Хранение и производительность Unicode"

Edit

Недавний вопрос SO, подчеркивающий, насколько плохая производительность nvarchar может быть...

SQL Server использует высокий CPU при поиске внутри строк nvarchar

Ответ 3

Будьте последовательны! Присоединение VARCHAR к NVARCHAR имеет большой успех.

Ответ 4

nvarchar будет иметь значительные накладные расходы в памяти, хранилище, рабочем наборе и индексировании, поэтому, если спецификации требуют, чтобы он действительно был никогда, не беспокойтесь.

У меня не было бы жесткого и быстрого правила "всегда nvarchar", потому что во многих ситуациях это может быть полная трата - особенно ETL из ASCII/EBCDIC или идентификаторов и столбцов кода, которые часто являются ключами и внешними ключами.

С другой стороны, существует множество случаев столбцов, где я бы сразу задал этот вопрос, и если бы я не получил быстрый и быстрый ответ сразу, я бы сделал столбец nvarchar.

Ответ 5

Для вашего приложения nvarchar в порядке, потому что размер базы данных невелик. Высказывание "всегда использовать nvarchar" - это огромное упрощение. Если вам не требуется хранить вещи, такие как кандзи или другие сумасшедшие персонажи, используйте VARCHAR, он будет использовать намного меньше места. Мой предшественник в моей нынешней работе проектировал что-то, используя NVARCHAR, когда это не было необходимо. Недавно мы переключили его на VARCHAR и сохранили 15 ГБ только на той таблице (она была написана в основном). Кроме того, если у вас есть индекс в этой таблице, и вы хотите включить этот столбец или создать составной индекс, вы просто увеличили размер вашего индексного файла.

Просто будьте вдумчивы в своем решении; в SQL-разработке и определениях данных, похоже, редко бывает "ответ по умолчанию" (кроме, конечно, избегайте курсоров любой ценой).

Ответ 6

Поскольку ваше приложение мало, по существу нет значительного увеличения стоимости использования nvarchar над varchar, и вы сбережете себе потенциальные головные боли в будущем, если вам нужно хранить данные Unicode.

Ответ 7

В течение последних нескольких лет все наши проекты использовали NVARCHAR для всего, поскольку все эти проекты являются многоязычными. Импортированные данные из внешних источников (например, файл ASCII и т.д.) Преобразуются в Юникод перед его вставкой в ​​базу данных.

Мне еще предстоит столкнуться с любыми проблемами, связанными с производительностью, из более крупных индексов и т.д. Индексы используют больше памяти, но память дешевая.

Если вы используете хранимые процедуры или строите SQL на лету, убедитесь, что все строковые константы имеют префикс N (например, SET @foo = N'Hello world. ';), поэтому константа также является Unicode. Это позволяет избежать преобразования любого типа во время выполнения.

YMMV.

Ответ 8

Я могу говорить по этому опыту, остерегайтесь nvarchar. Если вы абсолютно не требуете этого, этот тип поля данных разрушает производительность в большей базе данных. Я унаследовал базу данных, которая была повреждена с точки зрения производительности и пространства. Мы смогли уменьшить размер базы данных 30 ГБ на 70%! Были некоторые другие изменения, сделанные, чтобы помочь с производительностью, но я уверен, что varchar также значительно помог с этим. Если ваша база данных имеет потенциал для роста таблиц до миллиона записей, всегда избегайте nvarchar любой ценой.

Ответ 9

Вообще говоря; Начните с самого дорогого типа данных, который имеет наименьшие ограничения. Поместите его в производство. Если производительность начинает быть проблемой, узнайте, что на самом деле хранится в этих столбцах nvarchar. Есть ли там персонажи, которые не вписывались бы в varchar? Если нет, переключитесь на varchar. Не пытайтесь предварительно оптимизировать, прежде чем вы узнаете, где боль. Я предполагаю, что выбор между nvarchar/varchar - это не то, что замедлит ваше приложение в перспективном будущем. Там будут другие части приложения, где настройка производительности даст вам гораздо больше ударов по долларам.

Ответ 10

Я смущаюсь добавить еще один ответ здесь, поскольку их уже немало, но нужно сделать несколько моментов, которые не были сделаны или не были сделаны четко.

Сначала: Не всегда используйте NVARCHAR. Это очень опасный и часто дорогостоящий подход/подход. И не лучше сказать "Никогда не использовать курсоры", поскольку они иногда являются наиболее эффективным средством решения конкретной проблемы, а общая работа вокруг цикла WHILE будет почти всегда медленнее, чем правильно выполненный Курсор.

Единственный раз, когда вы должны использовать термин "всегда", советуем "всегда делать то, что лучше для ситуации". Конечно, это трудно определить, особенно когда вы пытаетесь сбалансировать краткосрочные выгоды во время разработки (менеджер: "нам нужна эта функция, о которой вы не знали, пока только сейчас - неделю назад!" ) С длинными -терминальные затраты на обслуживание (менеджер, который изначально оказал давление на команду, чтобы завершить трехмесячный проект в трехнедельном спринте: "Почему у нас такие проблемы с производительностью? Как мы могли бы сделать X, который не имеет гибкости? Мы не можем себе позволить спринт или два, чтобы исправить это. Что мы можем сделать за неделю, чтобы мы могли вернуться к нашим приоритетным статьям? И нам определенно нужно больше времени на разработку, чтобы это не продолжалось!" ).

Второй: @gbn ответ затрагивает некоторые очень важные моменты, которые следует учитывать при принятии определенных решений моделирования данных, когда путь не на 100% ясен. Но есть еще больше, чтобы рассмотреть:

  • размер файлов журнала транзакций
  • время, необходимое для репликации (при использовании репликации)
  • время, которое требуется для ETL (если ETLing)
  • время, необходимое для отправки журналов в удаленной системе и восстановления (при использовании логарифмической доставки)
  • размер резервных копий
  • требуется время, необходимое для завершения резервного копирования
  • требуется время, необходимое для восстановления (это может быть важно в какой-то день;)
  • размер, необходимый для tempdb
  • производительность триггеров (для вставленных и удаленных таблиц, которые хранятся в tempdb)
  • производительность управления версиями строк (при использовании SNAPSHOT ISOLATION, поскольку хранилище версий находится в tempdb)
  • возможность получить новое дисковое пространство, когда финансовый директор говорит, что в прошлом году он потратил 1 миллион долларов на SAN, и поэтому они не разрешат еще $250 тыс. для дополнительного хранения
  • продолжительность выполнения операций INSERT и UPDATE
  • требуется время, необходимое для обслуживания индексов
  • и т.д. и т.д.

Утраченное пространство имеет каскадный эффект огромный для всей системы. Я написал статью, в которой явствует подробная информация по этой теме: Диск дешево! ORLY? (требуется бесплатная регистрация, извините, я не контролирую эту политику).

Третий: Хотя некоторые ответы неверно фокусируются на аспекте "это небольшое приложение", а некоторые правильно предлагают "использовать то, что подходит", ни один из ответов не дал реальных указаний к OP Важная деталь, упомянутая в Вопросе, заключается в том, что это веб-страница для их школы. Большой! Поэтому мы можем предположить, что:

  • Поля для имен студентов и/или факультетов должны быть NVARCHAR, поскольку со временем становится все более вероятным появление в этих местах имен из других культур.
  • Но для названия улицы и города? Цель приложения не была указана (это было бы полезно), но при условии, что адресные записи, если таковые имеются, относятся только к определенному географическому региону (то есть к одному языку/культуре), затем используйте VARCHAR с соответствующим кодом Страница (которая определяется из столбца поля).
  • Если вы сохраняете коды состояния и/или страны ISO (нет необходимости хранить INT/TINYINT, так как ISO-коды фиксированной длины, читаемые человеком и хорошо, стандартные) используйте CHAR(2) для двухбуквенных кодов и CHAR(3) при использовании трехбуквенных кодов.
  • Если вы храните почтовые коды (например, почтовые индексы), используйте VARCHAR, поскольку это международный стандарт, чтобы никогда не использовать букву вне A-Z. И да, по-прежнему используйте VARCHAR, даже если только хранить почтовые индексы США, а не INT, поскольку почтовые индексы не являются числами, они являются строками, а некоторые из них имеют ведущее "0".
  • Если вы сохраняете адреса электронной почты и/или URL-адреса, используйте NVARCHAR, так как оба из них теперь могут содержать символы Unicode.
  • и т.д....

Четвертое:Теперь, когда у вас есть NVARCHAR данные, занимающие в два раза больше места, чем нужно для данных, которые хорошо вписываются в VARCHAR ( "подходит красиво" = не превращается в "?" ) И каким-то образом, как по волшебству, приложение действительно увеличилось, и теперь есть, по крайней мере, одно из этих полей миллионы записей, в которых большинство строк являются стандартными ASCII, но некоторые содержат символы Unicode, поэтому вам нужно оставить NVARCHAR, рассмотрите следующее:

  • Если вы используете SQL Server 2008 или новее и находитесь в Enterprise Edition, вы можете включить Сжатие данных. Сжатие данных может (но не "всегда" ) сжимать данные 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) данные, которые являются IN ROW (не вне строки на страницах LOB или OVERFLOW), могут быть сжаты как минимум PAGE, и, возможно, также сжатый ROW (не уверен в этом последнем).
    • Любые данные OFF ROW, LOB или OVERLOW = Без сжатия для вас!
  • Если вы используете версию старше 2008 года или нет в 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 type:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    

Ответ 11

Я часто занимаюсь этим вопросом на работе:

  • FTP-каналы инвентаризации и ценообразования - описания элементов и другой текст были в nvarchar, когда varchar работал нормально. Преобразование их в varchar уменьшило размер файла почти в два раза и действительно помогло с загрузками.

  • Вышеупомянутый сценарий работал нормально, пока кто-то не поместил специальный символ в описание элемента (возможно, товарный знак, не помню)

Я все еще не использую nvarchar каждый раз над varchar. Если есть какие-либо сомнения или возможности для специальных символов, я использую nvarchar. Я считаю, что я использую varchar в основном, когда я на 100% контролирую, что заполняет поле.

Ответ 12

Почему во всей этой дискуссии не было упоминания о UTF-8? Возможность сохранять полный диапазон символов в Юникоде не означает, что нужно всегда выделять два байта на символ (или "кодовую точку" для использования термина UNICODE). Все ASCII - UTF-8. Проверяет ли SQL Server поля VARCHAR(), что текст является строгим ASCII (то есть верхний байт бит 0)? Надеюсь, что нет.

Если вы хотите сохранить юникод и хотите совместимость со старыми ASCII-приложениями, я бы подумал, что использование VARCHAR() и UTF-8 было бы волшебной пулей: он использует больше пространства, когда это необходимо.

Для тех из вас, кто не знаком с UTF-8, могу ли я рекомендовать праймер.

Ответ 13

Будут исключительные случаи, когда вы захотите преднамеренно ограничить тип данных, чтобы убедиться, что он не содержит символов из определенного набора. Например, у меня был сценарий, в котором мне нужно было сохранить имя домена в базе данных. В то время интернационализация доменных имен была ненадежной, поэтому было лучше ограничить ввод данных на базовом уровне и помочь избежать возможных проблем.

Ответ 14

Если вы используете NVARCHAR только потому, что это требует системная хранимая процедура, наиболее частым явлением является необъяснимое sp_executesql, а ваш динамический SQL очень длинный, вам будет лучше с точки зрения производительности делать все строковые манипуляции ( конкатенация, замена и т.д.) в VARCHAR, затем преобразование конечного результата в NVARCHAR и подача его в параметр proc. Поэтому нет, не всегда используйте NVARCHAR!