Які основні відмінності у продуктивності між типами даних varchar і nvarchar SQL Server?


236

Я працюю над базою даних для невеликого веб-додатка в моїй школі SQL Server 2005.
Я бачу пару шкіл думки з питання varcharvs nvarchar:

  1. Використовуйте, varcharякщо ви не маєте справу з великою кількістю інтернаціоналізованих даних, тоді використовуйте nvarchar.
  2. Просто використовуйте nvarcharдля всього.

Я починаю бачити достоїнства погляду 2. Я знаю, що nvarchar займає вдвічі більше місця, але це не обов'язково величезна угода, оскільки це збирається лише для зберігання даних для кількох сотень студентів. Мені здається, що було б найпростіше не хвилюватися про це і просто дозволити всім використовувати nvarchar. Або щось мені не вистачає?


Аналогічне питання тут: stackoverflow.com/questions/312170 / ... EDIT по лю dorfier: який цікаво прийшов до прямо протилежних висновків.
Booji Boy

6
посилання значно більш обширна нитка, яка прийшла до протилежного висновку. stackoverflow.com/questions/312170 / ...
dkretz

2
Джейсон: Я сподіваюся, що це невідповідний запит, але ви можете, будь ласка, розглянути можливість зміни прийнятої відповіді на відповіді на gbn . Відповідь Джобарона з багатьох причин жахливо помилкова. Якщо це буде "прийнято", вводить в оману новачків, які роблять поганий вибір. "Завжди використовувати NVARCHAR" це зайве та марно , і це може мати дуже негативний вплив на продуктивність та апаратні витрати / бюджети. Кілька рядів, навіть кілька тисяч, не мають значення. Але системи зростають швидше, ніж очікують люди, тож прийнята відповідь сьогодні є корисною для громади. Дякую.
Соломон Руцький

Відповіді:


140

Завжди використовуйте нварчар.

Можливо, вам ніколи не знадобляться двобайтові символи для більшості програм. Однак якщо вам потрібно підтримувати двобайтові мови і у вас є лише однобайтова підтримка у вашій схемі бази даних, повертатись та змінюватись у всій програмі дійсно дорого.

Вартість міграції однієї програми з varchar на nvarchar буде набагато більше, ніж небагато додаткового дискового простору, яке ви використовуєте в більшості програм.


4
набагато важче повернутися назад і додати підтримку багатомовних текстів / повідомлень, часових поясів, одиниць виміру та валюти, тому кожен ОБОВ'ЯЗКОВО повинен кодувати це у своїй заявці з першого дня, ВЖЕ ВАЖИ (навіть якщо це лише на веб-сторінці вашої домашньої сторінки) додаток)!
КМ.

82
Що щодо розміру індексу, використання пам'яті тощо? Я припускаю, що ти завжди використовуєш int, коли ти можеш використовувати tinyint занадто "про всяк випадок"?
gbn

99
Завжди кодування / планування багатомовного сайту (коли у вас немає жодної друкарні, що вам коли-небудь знадобиться) - це як сказати всім молодим дорослим, що вони повинні придбати великий 8-сидіння, бензиновий позашляховик для свого першого автомобіля ... адже , вони можуть одружитися одного дня і можуть мати 6 дітей,. Я вважаю за краще насолоджуватися продуктивністю та ефективністю, хоча можу і заплатити ціну за оновлення, коли / якщо мені це потрібно.
EJ Brennan

4
@cbmeeks: Я не кодую те, що не знаю. Але якщо ви можете використовувати його без помітного хіт-показника, то ваші бази даних не є достатньо великими, щоб мати значення ...
gbn

60
Зазвичай, коли люди починають свою відповідь словом "Завжди", то слід ігнорувати все, що відбувається після цього. (Зверніть увагу, я розпочав цю заяву зі слова "зазвичай" :)
Брендон Мур

226

Дисковий простір - це не проблема ... але пам'ять та продуктивність будуть. Подвійне читання сторінки, подвійний розмір індексу, дивний ЛАЙК і = постійна поведінка тощо

Чи потрібно зберігати китайський тощо сценарій? Так чи ні...

І від MS BOL " Ефекти зберігання та продуктивності Unicode "

Редагувати :

Нещодавнє запитання про те, що підкреслює, наскільки поганими можуть бути nvarchar ...

SQL Server використовує високий процесор при пошуку всередині рядків nvarchar


19
+1, якщо ваш додаток стане міжнародним, у вас виникнуть багато інших проблем, які потрібно турбувати з приводу пошуку / заміни на nvarchar: багатомовний текст / повідомлення, часові пояси, одиниці виміру та валюта
KM.

2
Але що робити, якщо іноді вам потрібно зберігати іноземне ім’я, як Хосе чи Бьорн?
Qwertie

7
@Qwertie: тоді ви використовуєте nvarchar. Що ви цього не робите, використовуйте це без потреби. Ці 2 імені все одно вписуються у
варчар

6
Скажіть, що місце на диску не є проблемою, стосується не всіх. Ми наївно використовували nvarchar без необхідності у великій банківській програмі з мільярдами записів, що зберігаються протягом багатьох років. З дорогим сховищем на базі SAN з реплікацією, резервним копією та відновленням після аварій це може насправді призвести до мільйонів доларів витрат на nvarchar vs varchar. Не кажучи вже про велику (100%) ефективність продуктивності, яку потрібно прочитати вдвічі більше байтів з диска за кожне прочитане.
codemonkey

2
@codemonkey та ін: Я зробив усе, що міг, щоб вирішити проблему витраченого простору цілісно в наступній статті: Диск дешевий! ORLY? (хоча безкоштовна реєстрація потрібна). Ця стаття покликана допомогти запобігти ситуації, з якою стикалися кодемонські ключі щодо дорогого зберігання на рівні підприємств.
Соломон Руцький

59

Будьте послідовними! ПРИЄДНАЙТЕСЬ ВАРХАР до NVARCHAR - це великий хіт продуктивності.


115
Якщо ви приєднуєтесь до полів символів, то, можливо, у вашій базі даних є гірші проблеми, ніж використання nvarchar або varchar, взагалі.
Брендон Мур

@Thomas Харлан Простого тест показує мені , що немає ніякої відчутної різниці між приєднанням nvarcharдо varcharпроти перетворення nvarcharдо varcharі приєднання до varchar. Якщо, звичайно, ви не мали на увазі послідовності в типах даних стовпців, а не приєднання.
ajeh

1
@ajeh та Thomas: 1) "прості" тести часто вводять в оману, оскільки вони не охоплюють варіацій, які викликають розбіжності в поведінці. 2) Якщо людина бачить різке падіння продуктивності при змішуванні VARCHARі NVARCHAR, що має бути пов'язане з індексацією VARCHARколони разом з типом сортування , використовуваної для цього стовпця (а отже , і індексу). Я детально висвітлюю цю тему в наступній публікації блогу: Вплив на індекси при змішуванні типів VARCHAR і NVARCHAR .
Соломон Руцький

44

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

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

З іншого боку, є безліч випадків стовпців, де я б обов'язково поставив це питання рано, і якщо я не отримав важкої та швидкої відповіді, я зробив би колонку нварчар.


26

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

По- перше: Do НЕ завжди використовувати NVARCHAR. Це дуже небезпечне і часто затратне ставлення / підхід. І не краще сказати " Ніколи не використовуйте курсори", оскільки вони іноді є найбільш ефективним засобом вирішення певної проблеми, і загальна робота навколо WHILEциклу майже завжди буде повільнішою, ніж правильно виконаний Курсор.

Єдиний час, коли ви повинні використовувати термін «завжди», - це, коли радить «завжди робити те, що найкраще для ситуації». Зазначимо, що часто важко визначити, особливо, коли намагаються збалансувати короткочасні вигоди у часі розвитку (менеджер: "нам потрібна ця функція - про яку ти досі не знав - тиждень тому!") З довгими -строкові витрати на обслуговування (менеджер, який спочатку тиснув на команду, щоб виконати тримісячний проект у 3-тижневому спринті: "чому у нас виникають ці проблеми з продуктивністю? Як ми могли зробити X, який не має гнучкості? Ми не можемо собі дозволити спринт або два, щоб виправити це. Що ми можемо зробити за тиждень, щоб ми могли повернутися до наших пріоритетних пунктів? І нам, безумовно, потрібно витратити більше часу на дизайн, щоб це не продовжувалося! ").

По-друге: відповідь @ gbn стосується деяких дуже важливих моментів, які слід враховувати при прийнятті певних рішень щодо моделювання даних, коли шлях не є на 100% зрозумілим. Але є ще більше, що слід врахувати:

  • розмір файлів журналу транзакцій
  • час, необхідний для копіювання (якщо використовується реплікація)
  • час, який потрібно на ETL (якщо ETLing)
  • час, який потрібен для доставки журналів до віддаленої системи та відновлення (якщо використовується журнал доставки)
  • розмір резервного копіювання
  • проміжок часу, необхідний для завершення резервного копіювання
  • тривалість часу, необхідного для відновлення (це може бути важливо одного дня ;-)
  • розмір, необхідний для tempdb
  • виконання тригерів (для вставлених та видалених таблиць, які зберігаються у tempdb)
  • виконання версії версій (якщо використовується SNAPSHOT ISOLATION, оскільки сховище версій знаходиться в tempdb)
  • можливість отримати новий дисковий простір, коли фінансовий директор каже, що щойно вони витратили 1 мільйон доларів на SAN в минулому році, і тому вони не дозволять отримати ще $ 250 000 для додаткового зберігання
  • тривалість часу, необхідного для виконання 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оскільки вони можуть містити символи Unicode.
  • і так далі....

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

  1. Якщо ви використовуєте RTM SQL Server 2008 - 2016 і перебуваєте на Enterprise Edition, АБО якщо ви використовуєте SQL Server 2016 SP1 (який зробив стиснення даних доступним у всіх виданнях) або новішим, ви можете ввімкнути стиснення даних . Стиснення даних може (але не "завжди") стискати дані Unicode у NCHARта NVARCHARполях. Визначальними чинниками є:

    1. NCHAR(1 - 4000)і NVARCHAR(1 - 4000)використовуйте Стандартну схему стиснення для Unicode , але починаючи лише з SQL Server 2008 R2, І тільки для даних IN ROW, а не ЗОВНІШНЬО! Здається, це краще, ніж звичайний алгоритм стиснення ROW / PAGE.
    2. NVARCHAR(MAX)і XML(і, мабуть, також VARBINARY(MAX), TEXTі NTEXT) дані, які є В РАДУ (не в рядку на сторінках LOB або OVERFLOW), можна принаймні стискати PAGE, але не стискати ROW. Звичайно, стиснення PAGE залежить від розміру значення рядка: я протестував VARCHAR (MAX) і побачив, що 6000 рядків / байтових рядків не стискатимуться, але 4000 рядків / байт рядків.
    3. Будь-які дані OFF ROW, LOB або OVERLOW = Без стиснення для вас!
  2. Якщо ви використовуєте RTM SQL Server 2005 або 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)
           );
  3. Ви можете GZIP вхідні значення в, VARBINARY(MAX)а потім розпаковувати на виході:

    • Для SQL Server 2005 - 2014: ви можете використовувати SQLCLR. SQL # (бібліотека SQLCLR, яку я писав) постачається з Util_GZip та Util_GUnzip у безкоштовній версії
    • Для SQL Server 2016 і новіших: ви можете використовувати вбудовані COMPRESSта DECOMPRESSфункції, які також є GZip.
  4. Якщо ви використовуєте SQL Server 2017 або новішу версію, ви можете розглянути, як зробити таблицю індексом кластерних стовпців.

  5. Хоча це ще не є життєздатним варіантом, SQL Server 2019 представляє вбудовану підтримку UTF-8 в VARCHAR/ CHARтипах даних. Наразі в ньому занадто багато помилок, щоб їх можна було використовувати, але якщо вони виправлені, то це варіант для деяких сценаріїв. Перегляньте мій пост " Native UTF-8 підтримка в SQL Server 2019: рятівник чи помилковий пророк? ", Щоб отримати детальний аналіз цієї нової функції.


7
Повільний хлопання. Просто вражений, що "завжди використовуй нварчар" отримав 140 голосів, а це не зробило. Чудова робота над цією посадою.
шизоїд04

1
@ schizoid04 Дякую Чесно кажучи, прийнята відповідь була розміщена за 7 років до моєї, тому багато трафіку, який проголосував за неї (та / або різні інші), які ніколи не поверталися до переоцінки. І все-таки вона забезпечує дуже міцний контрапункт теорії "мудрості натовпу", яка керує форумами на основі голосування. Занадто багато дезінформації. Наприклад, це на DBA.SE. Інша відповідь, прийнята до того, як я розмістив шахту, "правильна" найменшими визначеннями, вводить в оману і містить інформацію, яку я спростовую в моїй, але вона все ще перевершує мою.
Соломон Руцький

22

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

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


10

Оскільки ваш додаток невеликий, істотно не збільшується вартість використання nvarchar над varchar, і ви заощадите потенційні головні болі в дорозі, якщо у вас є необхідність зберігати дані про unicode.


8

Загалом; Почніть з найдорожчого типу даних, який має найменші обмеження. Покладіть його у виробництво . Якщо продуктивність починає бути проблемою, з’ясуйте, що насправді зберігається в цих nvarcharстовпцях. Чи є там якісь символи, які б не вписалися varchar? Якщо ні, перейдіть на varchar. Не намагайтеся заздалегідь оптимізувати, перш ніж дізнаєтесь, де болить. Я здогадуюсь, що вибір між nvarchar / varchar - це не те, що сповільнить вашу програму в передбачуваному майбутньому. Будуть і інші частини програми, де налаштування продуктивності дасть вам набагато більше ударів за долари .


7

За останні кілька років усі наші проекти використовували NVARCHAR для всього, оскільки всі ці проекти багатомовні. Імпортовані дані із зовнішніх джерел (наприклад, файл ASCII тощо) перетворюються в Unicode перед тим, як вставити в базу даних.

Мені ще не доводилося стикатися з будь-якими проблемами, пов'язаними з ефективністю від більших індексів тощо. Індекси використовують більше пам'яті, але пам'ять дешева.

Якщо ви використовуєте збережені процедури або будуєте SQL на льоту, переконайтесь, що всі рядкові константи мають префікс N (наприклад, SET @foo = N'Hello world. ';), Тому константа також є Unicode. Це дозволяє уникнути будь-якого перетворення типу рядка під час виконання.

YMMV.


4
У таблицях, з якими ти працюєш, ти, мабуть, не має кількох сотень мільйонів записів. Я погоджуюся, що для більшості додатків дефолт до nvarchar - це добре, але не для всіх.
Брендон Мур

7

Я можу говорити з цього досвіду, будьте обережні nvarchar. Якщо ви абсолютно цього не вимагаєте, тип цього типу даних знищує продуктивність на більшій базі даних. Я успадкував базу даних, яка завдала шкоди з точки зору продуктивності та простору. Нам вдалося зменшити розмір бази даних на 30 ГБ на 70%! Були зроблені деякі інші модифікації, які допомогли досягти продуктивності, але я впевнений, varcharщо вони також значно допомогли в цьому. Якщо у вашій базі даних є потенціал для збільшення таблиць до мільйона + записів, залишайтеся подалі nvarcharза будь-яку ціну.


4

Я часто працюю з цим питанням на роботі:

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

  • Вищеописаний сценарій спрацював чудово, поки хтось не вклав спеціальний символ в опис предмета (можливо, торгова марка, не пам'ятаю)

Я все ще не використовую nvarchar кожен раз над варчаром. Якщо є якісь сумніви чи потенціал для спеціальних символів, я використовую nvarchar. Я вважаю, що я в основному використовую варчар, коли я на 100% контролюю те, що заселяє поле.


3

Чому в усій цій дискусії не було згадки про UTF-8? Наявність можливості зберігати повний проміжок символів unicode не означає, що потрібно завжди виділяти два байти на символ (або "кодову точку", щоб використовувати термін UNICODE). Весь ASCII є UTF-8. Чи перевіряє SQL Server на поля VARCHAR (), що текст є строгим ASCII (тобто верхній нульовий біт)? Я би сподівався, що ні.

Якщо тоді ви хочете зберігати unicode і хочете сумісності зі старими програмами, призначеними лише для ASCII, я б подумав, що використання VARCHAR () та UTF-8 було б магічною кулею: вона використовує більше місця, коли потрібно.

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


2
Те, що ви пропонуєте, може працювати в деяких додатках, але слід також врахувати вплив додаткового шару кодування на спосіб обробки тексту SQL. Зокрема, будуть здійснені порівняння, пошук та відповідність шаблонів. І якщо звіти запускаються проти бази даних, стандартні засоби звітності не будуть втручатися в багатобайтові символи правильно. І може бути здійснено основний імпорт та експорт. Я думаю, що - в довгостроковій перспективі - ця схема може мати більше клопотів, ніж це коштує.
Джеффрі Л Уітлідж

1
Зберігати UTF-8 у стовпцях VARCHAR неможливо. MSSQL завжди буде перетворювати ваші дані UTF-8 у зіставку стовпців. Якщо ви зіпсуєте зіставлення (наприклад, намагаючись зберегти CP1252 в Latin_1), конверсія не буде працювати, і ви отримаєте зайві байти у своїх даних. Може здатися, що це добре спрацює, коли ви перетворите латину_1 на UTF-8 (на стороні програми) і знову на латину_1 (db), але це лише ілюзія. Ви можете прокрастись за допомогою автоматичного перетворення БД у зіставлення стовпців, використовуючи freetds та встановивши протокол на щось менше 7, але ви втратите можливість запитувати nvarchar.
chugadie

1
@chugadie і Tevya: ця відповідь є дещо безглуздою. SQL Server використовує лише UCS-2 / UTF-16 для зберігання даних Unicode (тобто Nтипів XML та -prefixed). Ви не можете вибрати вибір UTF-8. Також кодування Unicode (UTF-8, UCS-2 / UTF-16 та UTF-32) не можна застосовувати до полів VARCHAR.
Соломон Руцький

2

Будуть виняткові випадки, коли ви хочете навмисно обмежити тип даних, щоб переконатися, що він не містить символів певного набору. Наприклад, у мене був сценарій, коли мені потрібно було зберігати доменне ім’я в базі даних. Інтернаціоналізація доменних імен на той час не була надійною, тому було краще обмежити вхід на базовому рівні та допомогти уникнути можливих проблем.


1

Якщо ви використовуєте NVARCHARлише тому, що цього вимагає системна збережена процедура, найчастіше це явище є незрозумілим sp_executesql, а ваш динамічний SQL дуже довгий, вам краще буде з точки зору продуктивності виконувати всі рядкові маніпуляції (конкатенація, заміна тощо) при VARCHARперетворенні. кінцевий результат до NVARCHARта введення його в параметр proc. Так що ні, не завжди використовуйте NVARCHAR!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.