Database
 sql >> база данни >  >> RDS >> Database

SQL VARCHAR Тип данни, които трябва и не трябва за по-бързи бази данни

Тази публикация има „прикачени низове:по добра причина. Ще проучим дълбоко в SQL VARCHAR, типът данни, който се занимава с низове.

Освен това това е „само за вашите очи“, защото без низове няма да има публикации в блогове, уеб страници, инструкции за игри, запазени рецепти и много повече, за да могат очите ни да четат и да се наслаждават. Ние се занимаваме с един милион струни всеки ден. Така че, като разработчици, вие и аз сме отговорни да направим този вид данни ефективни за съхранение и достъп.

Имайки това предвид, ще разгледаме най-важното за съхранението и производителността. Въведете какво трябва и не трябва за този тип данни.

Но преди това VARCHAR е само един от типовете низове в SQL. Какво го прави различен?

Какво е VARCHAR в SQL? (С примери)

VARCHAR е низ или символен тип данни с различен размер. Можете да съхранявате букви, цифри и символи с него. Започвайки със SQL Server 2019, можете да използвате пълния набор от символи на Unicode, когато използвате съпоставяне с поддръжка на UTF-8.

Можете да декларирате VARCHAR колони или променливи, като използвате VARCHAR[(n)], където n означава размера на низа в байтове. Диапазонът от стойности за n е от 1 до 8000. Това са много данни за знаци. Но дори повече, можете да го декларирате с помощта на VARCHAR(MAX), ако имате нужда от гигантски низ до 2GB. Това е достатъчно голямо за вашия списък с тайни и лични неща в дневника ви! Обърнете внимание обаче, че можете да го декларирате и без размера и по подразбиране той е 1, ако го направите.

Нека имаме пример.

DECLARE @actor VARCHAR(20) = 'Robert Downey Jr.';
DECLARE @movieCharacter VARCHAR(10) = 'Iron Man';

DECLARE @movie VARCHAR = 'Avengers';

SELECT @actor, @movieCharacter, @movie 

На фигура 1 първите 2 колони имат определени размери. Третата колона остава без размер. И така, думата „Отмъстителите“ е съкратена, защото VARCHAR без деклариран размер по подразбиране е 1 знак.

Сега, нека опитаме нещо огромно. Но имайте предвид, че изпълнението на тази заявка ще отнеме известно време – 23 секунди на моя лаптоп.

-- This will take a while
DECLARE @giganticString VARCHAR(MAX);

SET @giganticString = REPLICATE(CAST('kage bunshin no jutsu' AS VARCHAR(MAX)),100000000)

SELECT DATALENGTH(@giganticString)

За да генерираме огромен низ, репликирахме kage bunshin no jutsu 100 милиона пъти. Обърнете внимание на CAST в REPLICATE. Ако не CAST низовия израз към VARCHAR(MAX), резултатът ще бъде съкратен само до 8000 знака.

Но как SQL VARCHAR се сравнява с други типове низови данни?

Разлика между CHAR и VARCHAR в SQL

В сравнение с VARCHAR, CHAR е символен тип данни с фиксирана дължина. Без значение колко малка или голяма стойност поставите на променлива CHAR, крайният размер е размерът на променливата. Проверете сравненията по-долу.

DECLARE @tvSeriesTitle1 VARCHAR(20) = 'The Mandalorian';
DECLARE @tvSeriesTitle2 CHAR(20) = 'The Mandalorian';

SELECT DATALENGTH(@tvSeriesTitle1) AS VarcharValue,
       DATALENGTH(@tvSeriesTitle2) AS CharValue

Размерът на низа “The Mandalorian” е 15 знака. И така, VarcharValue колоната го отразява правилно. Въпреки това, CharValue запазва размера на 20 – допълнен е с 5 интервала вдясно.

SQL VARCHAR срещу NVARCHAR

Две основни неща идват на ум, когато сравнявате тези типове данни.

Първо, това е размерът в байтове. Всеки знак в NVARCHAR има два пъти по-голям размер от VARCHAR. NVARCHAR(n) е само от 1 до 4000.

След това знаците, които може да съхранява. NVARCHAR може да съхранява многоезични знаци като корейски, японски, арабски и т.н. Ако планирате да съхранявате корейски K-Pop текстове във вашата база данни, този тип данни е една от вашите опции.

Нека имаме пример. Ще използваме K-pop групата 세븐틴 или Seventeen на английски.

DECLARE @kpopGroupKorean NVARCHAR(5) = N'세븐틴';

SELECT @kpopGroupKorean AS KPopGroup, 
       DATALENGTH(@kpopGroupKorean) AS SizeInBytes,
       LEN(@kpopGroupKorean) AS [NoOfChars]

Горният код ще изведе стойността на низа, неговия размер в байтове и броя на знаците. Ако това са не-Unicode знаци, броят на знаците е равен на размера в байтове. Но това не е така. Вижте фигура 4 по-долу.

Виждаш ли? Ако NVARCHAR има 3 знака, размерът в байтове е два пъти. Но не и с VARCHAR. Същото важи и ако използвате английски знаци.

Но какво ще кажете за NCHAR? NCHAR е двойникът на CHAR за Unicode знаци.

SQL сървър VARCHAR с поддръжка на UTF-8

VARCHAR с поддръжка на UTF-8 е възможен на ниво сървър, ниво база данни или ниво колона на таблица чрез промяна на информацията за съпоставяне. Използваното съпоставяне трябва да поддържа UTF-8.

СЪБОРЯВАНЕ НА СЪРВЪР SQL

Фигура 5 представя прозореца в SQL Server Management Studio, който показва сортиране на сървъра.

СЪБРАНЕ НА БАЗА ДАННИ

Междувременно Фигура 6 показва съпоставянето на AdventureWorks база данни.

СЪБОРЯВАНЕ НА КОЛОНА НА ТАБЛИЦА

Съпоставянето на сървъра и базата данни по-горе показва, че UTF-8 не се поддържа. Низът за съпоставяне трябва да има _UTF8 в него за поддръжка на UTF-8. Но все пак можете да използвате поддръжка на UTF-8 на ниво колона на таблица. Вижте примера.

CREATE TABLE SeventeenMemberList
(
	id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
	KoreanName VARCHAR(20) COLLATE Latin1_General_100_BIN2_UTF8 NOT NULL,
	EnglishName VARCHAR(20) NOT NULL
)

Горният код има Latin1_General_100_BIN2_UTF8 съпоставяне за корейско име колона. Въпреки че VARCHAR, а не NVARCHAR, тази колона ще приема знаци на корейски език. Нека вмъкнем някои записи и след това да ги прегледаме.

INSERT INTO SeventeenMemberList
(KoreanName, EnglishName)
VALUES 
 (N'에스쿱스','S.Coups')
,(N'원우','Wonwoo')
,(N'민규','Mingyu')
,(N'버논','Vernon')
,(N'우지','Woozi')
,(N'정한','Jeonghan')
,(N'조슈아','Joshua')
,(N'도겸','DK')
,(N'승관','Seungkwan')
,(N'호시','Hoshi')
,(N'준','Jun')
,(N'디에잇','The8')
,(N'디노','Dino')

SELECT * FROM SeventeenMemberList 
ORDER BY KoreanName
COLLATE Latin1_General_100_BIN2_UTF8

Използваме имена от групата Seventeen K-pop, използвайки корейски и английски колеги. За корейски знаци, обърнете внимание, че все пак трябва да поставите префикс на стойността с N , точно като това, което правите със стойностите на NVARCHAR.

След това, когато използвате SELECT с ORDER BY, можете да използвате и сортиране. Можете да наблюдавате това в примера по-горе. Това ще следва правилата за сортиране за посоченото съпоставяне.

СЪХРАНЕНИЕ НА VARCHAR С ПОДДРЪЖКА НА UTF-8

Но как става съхранението на тези знаци? Ако очаквате 2 байта на знак, тогава ви очаква изненада. Вижте фигура 8.

Така че, ако съхранението има голямо значение за вас, помислете за таблицата по-долу, когато използвате VARCHAR с поддръжка на UTF-8.

Знаци Размер в байтове
Ascii 0 – 127 1
Азписма, базирана на латиница, както и гръцки, кирилица, коптски, арменски, иврит, арабски, сирийски, Tāna и N’Ko 2
Източноазиатска писменост като китайски, корейски и японски 3
Знаци в диапазона 010000–10FFFF 4

Нашият пример за корейски е източноазиатски скрипт, така че е 3 байта на знак.

След като приключихме с описването и сравняването на VARCHAR с други типове низове, нека сега да покрием какво трябва и какво не трябва

Правите при използването на VARCHAR в SQL Server

1. Посочете размер

Какво може да се обърка без да посочите размера?

ОТСЪЖАВАНЕ НА СТРУНА

Ако ви мързи да посочите размера, ще настъпи съкращаване на низа. Вече видяхте пример за това по-рано.

ВЪЗДЕЙСТВИЕ ЗА СЪХРАНЕНИЕ И ИЗПЪЛНЕНИЕ

Друго съображение е съхранението и производителността. Трябва само да зададете правилния размер за вашите данни, не повече. Но как би могъл да знаеш? За да избегнете съкращаване в бъдеще, може просто да го зададете на най-големия размер. Това е VARCHAR(8000) или дори VARCHAR(MAX). И 2 байта ще бъдат съхранени както са. Същото е и с 2GB. Има ли значение?

Отговорът на това ще ни отведе до концепцията за това как SQL Server съхранява данни. Имам друга статия, която обяснява това подробно с примери и илюстрации.

Накратко, данните се съхраняват в 8KB страници. Когато ред с данни надвиши този размер, SQL Server го премества в друга единица за разпределение на страници, наречена ROW_OVERFLOW_DATA.

Да предположим, че имате 2-байтови VARCHAR данни, които може да отговарят на оригиналната единица за разпределение на страници. Когато съхранявате низ, по-голям от 8000 байта, данните ще бъдат преместени на страницата с препълване на ред. След това го свийте отново до по-нисък размер и той ще бъде преместен обратно към оригиналната страница. Движението напред-назад причинява много I/O и затруднение в производителността. Извличането на това от 2 страници вместо от 1 също се нуждае от допълнително I/O.

Друга причина е индексирането. VARCHAR(MAX) е голямо НЕ като индексен ключ. Междувременно VARCHAR(8000) ще надвиши максималния размер на индексния ключ. Това е 1700 байта за неклъстерирани индекси и 900 байта за клъстерирани индекси.

ВЪЗДЕЙСТВИЕ НА КОНВЕРСИЯТА НА ДАННИ

И все пак има още едно съображение:преобразуване на данни. Опитайте го с CAST без размера като кода по-долу.

SELECT 
 SYSDATETIMEOFFSET() AS DateTimeInput
,CAST(SYSDATETIMEOFFSET() AS VARCHAR) AS ConvertedDateTime
,DATALENGTH(CAST(SYSDATETIMEOFFSET() AS VARCHAR)) AS ConvertedLength 

Този код ще извърши преобразуване на дата/час с информация за часовата зона във VARCHAR.

Така че, ако ни мързи да посочим размера по време на CAST или CONVERT, резултатът е ограничен само до 30 знака.

Какво ще кажете за конвертиране на NVARCHAR в VARCHAR с поддръжка на UTF-8? По-късно има подробно обяснение за това, така че продължавайте да четете.

2. Използвайте VARCHAR, ако размерът на низа варира значително

Имена от AdventureWorks базата данни се различават по размер. Едно от най-кратките имена е Мин Су, докато най-дългото име е Osarumwense Uwaifiokun Agbonile. Това е между 6 и 31 знака, включително интервалите. Нека импортираме тези имена в 2 таблици и да сравним между VARCHAR и CHAR.

-- Table using VARCHAR
CREATE TABLE VarcharAsIndexKey
(
	id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
	varcharName VARCHAR(50) NOT NULL
)
GO

CREATE INDEX IX_VarcharAsIndexKey_varcharName ON VarcharAsIndexKey(varcharName)
GO

-- Table using CHAR
CREATE TABLE CharAsIndexKey
(
	id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
	charName CHAR(50) NOT NULL
)
GO

CREATE INDEX IX_CharAsIndexKey_charName ON CharAsIndexKey(charName)
GO

INSERT INTO VarcharAsIndexKey (varcharName)
SELECT DISTINCT
LastName + ', ' + FirstName + ' ' + ISNULL(MiddleName,'')
FROM AdventureWorks.Person.Person 

INSERT INTO CharAsIndexKey (charName)
SELECT DISTINCT
LastName + ', ' + FirstName + ' ' + ISNULL(MiddleName,'')
FROM AdventureWorks.Person.Person 
GO

Кои от 2-те са по-добри? Нека проверим логическите показания, като използваме кода по-долу и инспектираме изхода на STATISTICS IO.

SET NOCOUNT ON
SET STATISTICS IO ON
SELECT id, varcharName
FROM VarcharAsIndexKey

SELECT id, charName
FROM CharAsIndexKey
SET STATISTICS IO OFF 

Логически чете:

Колкото по-малко логично се чете, толкова по-добре. Тук колоната CHAR използва повече от два пъти аналога VARCHAR. Така VARCHAR печели в този пример.

3. Използвайте VARCHAR като индексен ключ вместо CHAR, когато стойностите варират по размер

Какво се случи, когато се използва като индексни ключове? Ще се справи ли CHAR по-добре от VARCHAR? Нека използваме същите данни от предишния раздел и да отговорим на този въпрос.

Ще потърсим някои данни и ще проверим логическите показания. В този пример филтърът използва индексния ключ.

SET NOCOUNT ON
SET STATISTICS IO ON
SELECT varcharName FROM VarcharAsIndexKey 
WHERE varcharName = 'Sai, Adriana A' 
   OR varcharName = 'Rogers, Caitlin D'
SELECT charName FROM CharAsIndexKey  
WHERE charName = 'Sai, Adriana A' 
   OR charName = 'Rogers, Caitlin D'
SET STATISTICS IO OFF

Логически чете:

Следователно индексните ключове VARCHAR са по-добри от индексните ключове CHAR, когато ключът има различни размери. Но какво ще кажете за INSERT и UPDATE, които ще променят записите в индекса?

КОГАТО ИЗПОЛЗВАТЕ INSERT AND UPDATE

Нека тестваме 2 случая и след това да проверим логическите показания, както обикновено правим.

SET STATISTICS IO ON
INSERT INTO VarcharAsIndexKey (varcharName)
VALUES ('Ruffalo, Mark'), ('Johansson, Scarlett')

INSERT INTO CharAsIndexKey (charName)
VALUES ('Ruffalo, Mark'), ('Johansson, Scarlett')
SET STATISTICS IO OFF

Логически чете:

VARCHAR все още е по-добър при вмъкване на записи. Какво ще кажете за АКТУАЛИЗИРАНЕ?

SET STATISTICS IO ON
UPDATE VarcharAsIndexKey
SET varcharName = 'Hulk'
WHERE varcharName = 'Ruffalo, Mark'

UPDATE CharAsIndexKey
SET charName = 'Hulk'
WHERE charName = 'Ruffalo, Mark'
SET STATISTICS IO OFF

Логически чете:

Изглежда, че VARCHAR отново печели.

В крайна сметка печели нашия тест, макар че може да е малък. Имате ли по-голям тестов случай, който доказва обратното?

4. Помислете за VARCHAR с поддръжка на UTF-8 за многоезични данни (SQL Server 2019+)

Ако във вашата таблица има комбинация от Unicode и не-Unicode знаци, можете да помислите за VARCHAR с поддръжка на UTF-8 над NVARCHAR. Ако повечето от знаците са в диапазона от ASCII от 0 до 127, това може да предложи спестяване на място в сравнение с NVARCHAR.

За да разберете какво имам предвид, нека направим сравнение.

NVARCHAR КЪМ VARCHAR С ПОДДРЪЖКА UTF-8

Мигрирахте ли вече вашите бази данни към SQL Server 2019? Планирате ли да мигрирате вашите низови данни към UTF-8 съпоставяне? Ще имаме пример за смесена стойност на японски и неяпонски знаци, за да ви дадем представа.

CREATE TABLE NVarcharToVarcharUTF8
(
	NVarcharValue NVARCHAR(20) NOT NULL,
	VarcharUTF8 VARCHAR(45) COLLATE Latin1_General_100_BIN2_UTF8 NOT NULL
)

GO

INSERT INTO NVarcharToVarcharUTF8
(NVarcharValue, VarcharUTF8)
VALUES
(N'NARUTO-ナルト- 疾風伝',N'NARUTO-ナルト- 疾風伝');  -- NARUTO Shippûden

SELECT 
 NVarcharValue
,LEN(NVarcharValue) AS nvarcharNoOfChars 
,DATALENGTH(NVarcharValue) AS nvarcharSizeInBytes
,VarcharUTF8
,LEN(VarcharUTF8) AS varcharNoOfChars
,DATALENGTH(VarcharUTF8) AS varcharSizeInBytes
FROM NVarcharToVarcharUTF8

След като данните са зададени, ще проверим размера в байтове на 2-те стойности:

Изненада! При NVARCHAR размерът е 30 байта. Това е 15 пъти повече от 2 знака. Но когато се преобразува във VARCHAR с поддръжка на UTF-8, размерът е само 27 байта. Защо 27? Проверете как се изчислява това.

Така 9 от символите са по 1 байт всеки. Това е интересно, защото с NVARCHAR английските букви също са 2 байта. Останалите японски символи са по 3 байта.

Ако това са били всички японски символи, низът от 15 символа ще бъде 45 байта и също така ще използва максималния размер на VarcharUTF8 колона. Забележете, че размерът на NVarcharValue колоната е по-малка от VarcharUTF8 .

Размерите не могат да бъдат равни при конвертиране от NVARCHAR или данните може да не паснат. Можете да се обърнете към предишната таблица 1.

Помислете за въздействието върху размера, когато конвертирате NVARCHAR в VARCHAR с поддръжка на UTF-8.

Не трябва да използвате VARCHAR в SQL Server

1. Когато размерът на низа е фиксиран и не подлежи на нула, използвайте вместо това CHAR.

Общото правило, когато се изисква низ с фиксиран размер, е да се използва CHAR. Следвам това, когато имам изискване за данни, което се нуждае от десни интервали. В противен случай ще използвам VARCHAR. Имах няколко случая на употреба, когато трябваше да изхвърля низове с фиксирана дължина без разделители в текстов файл за клиент.

Освен това използвам колони CHAR само ако колоните няма да бъдат нула. Защо? Тъй като размерът в байтове на колоните CHAR, когато NULL е равен на определения размер на колоната. И все пак VARCHAR, когато NULL има размер 1, независимо колко е дефинираният размер. Изпълнете кода по-долу и вижте сами.

DECLARE @charValue CHAR(50) = NULL;
DECLARE @varcharValue VARCHAR(1000) = NULL;

SELECT 
 DATALENGTH(ISNULL(@charvalue,0)) AS CharSize
,DATALENGTH(ISNULL(@varcharvalue,0)) AS VarcharSize

2. Не използвайте VARCHAR(n) Ако n Ще надхвърли 8000 байта. Вместо това използвайте VARCHAR(MAX).

Имате ли низ, който ще надвишава 8000 байта? Това е моментът да използвате VARCHAR(MAX). Но за най-често срещаните форми на данни като имена и адреси, VARCHAR(MAX) е излишно и ще повлияе на производителността. В моя личен опит не си спомням изискване да използвам VARCHAR(MAX).

3. Когато използвате многоезични знаци със SQL Server 2017 и по-долу. Вместо това използвайте NVARCHAR.

Това е очевиден избор, ако все още използвате SQL Server 2017 и по-долу.

Дъното

Типът данни VARCHAR ни послужи добре за толкова много аспекти. Това беше за мен от SQL Server 7. Но понякога все още правим лош избор. В тази публикация SQL VARCHAR е дефиниран и сравнен с други типове низови данни с примери. И отново, ето какво трябва и не трябва да се прави за по-бърза база данни:

Направете:

  • Посочете размера n в VARCHAR[(n)] дори и да е по избор.
  • Използвайте го, когато размерът на низа варира значително.
  • Разглеждайте колоните VARCHAR като индексни ключове вместо CHAR.
  • И ако сега използвате SQL Server 2019, помислете за VARCHAR за многоезични низове с поддръжка на UTF-8.

Не трябва:

  • Не използвайте VARCHAR, когато размерът на низа е фиксиран и не подлежи на нула.
  • Не използвайте VARCHAR(n), когато размерът на низа надвишава 8000 байта.
  • И не използвайте VARCHAR за многоезични данни, когато използвате SQL Server 2017 и по-стари версии.

Имате ли още нещо да добавите? Уведомете ни в секцията за коментари. Ако смятате, че това ще помогне на приятелите ви разработчици, моля, споделете това в любимите си социални медийни платформи.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. ER диаграми в IRI Workbench

  2. Обединяване на файлове с данни със Statistica, част 1

  3. Как да изтриете ревизии на публикации с помощта на WP-CLI

  4. Как да възстановите база данни с Backup Manager

  5. Как да създадете модел на база данни от нулата