Количество байтов, используемых для символов Юникода в varchar - PullRequest
0 голосов
/ 20 февраля 2020

Распространенным заблуждением является думать, что CHAR (n) и VARCHAR (n), n определяет количество символов. Но в CHAR (n) и VARCHAR (n) n определяет длину строки в байтах (0-8000). n никогда не определяет количество символов, которые могут быть сохранены

Согласно этому заявлению от Microsoft, я предполагаю, что n - это длина данных строки, и когда мы храним символы Unicode в varchar, один символ должен занимать 2 байта. Но, когда я пытаюсь использовать образец, как показано ниже, я вижу varchar данные, берущие 1 байт вместо 2 байт.

declare @varchar varchar(6), @nvarchar nvarchar(6)

set @varchar = 'Ø'

select @varchar as VarcharString, len(@varchar) as VarcharStringLength, DATALENGTH(@varchar) as VarcharStringDataLength

Query Result

Может кто-нибудь объяснит причину этого?

Ответы [ 4 ]

1 голос
/ 20 февраля 2020

нашел время, чтобы проверить предположения моего первого ответа:

  • Создать базу данных с поддержкой UTF8

CREATE DATABASE [test-sc] COLLATE Latin1_General_100_CI_AI_KS_SC_UTF8

  • Создать таблица со всеми видами столбцов N / VARCHAR

CREATE TABLE [dbo].[UTF8Test]( [Id] [int] IDENTITY(1,1) NOT NULL, [VarcharText] [varchar](50) COLLATE Latin1_General_100_CI_AI NULL, [VarcharTextSC] [varchar](50) COLLATE Latin1_General_100_CI_AI_KS_SC NULL, [VarcharUTF8] [varchar](50) COLLATE Latin1_General_100_CI_AI_KS_SC_UTF8 NULL, [NVarcharText] [nvarchar](50) COLLATE Latin1_General_100_CI_AI_KS NULL, [NVarcharTextSC] [nvarchar](50) COLLATE Latin1_General_100_CI_AI_KS_SC NULL, [NVarcharUTF8] [nvarchar](50) COLLATE Latin1_General_100_CI_AI_KS_SC_UTF8 NULL)

  • Вставка данных испытаний из различных диапазонов Unicode

INSERT INTO [dbo].[UTF8Test] ([VarcharText],[VarcharTextSC],[VarcharUTF8],[NVarcharText],[NVarcharTextSC],[NVarcharUTF8]) VALUES ('a','a','a','a','a','a') INSERT INTO [dbo].[UTF8Test] ([VarcharText],[VarcharTextSC],[VarcharUTF8],[NVarcharText],[NVarcharTextSC],[NVarcharUTF8]) VALUES ('ö','ö','ö',N'ö',N'ö',N'ö') -- U+56D7 INSERT INTO [dbo].[UTF8Test] ([VarcharText],[VarcharTextSC],[VarcharUTF8],[NVarcharText],[NVarcharTextSC],[NVarcharUTF8]) VALUES (N'囗',N'囗',N'囗',N'囗',N'囗',N'囗') -- U+2000B INSERT INTO [dbo].[UTF8Test] ([VarcharText],[VarcharTextSC],[VarcharUTF8],[NVarcharText],[NVarcharTextSC],[NVarcharUTF8]) VALUES (N'?',N'?',N'?',N'?',N'?',N'?')

  • ВЫБРАТЬ длины

SELECT TOP (1000) [Id] ,[VarcharText] ,[VarcharTextSC] ,[VarcharUTF8] ,[NVarcharText] ,[NVarcharTextSC] ,[NVarcharUTF8] FROM [test-sc].[dbo].[UTF8Test] SELECT TOP (1000) [Id] ,LEN([VarcharText]) VT ,LEN([VarcharTextSC]) VTSC ,LEN([VarcharUTF8]) VU ,LEN([NVarcharText]) NVT ,LEN([NVarcharTextSC]) NVTSC ,LEN([NVarcharUTF8]) NVU FROM [test-sc].[dbo].[UTF8Test] SELECT TOP (1000) [Id] ,DATALENGTH([VarcharText]) VT ,DATALENGTH([VarcharTextSC]) VTSC ,DATALENGTH([VarcharUTF8]) VU ,DATALENGTH([NVarcharText]) NVT ,DATALENGTH([NVarcharTextSC]) NVTSC ,DATALENGTH([NVarcharUTF8]) NVU FROM [test-sc].[dbo].[UTF8Test]

SELECT lengths

Я был удивлен, обнаружив, что старая мантра «a VARCHAR хранит только однобайтовые символы» должна быть пересмотрена при использовании параметров сортировки UTF8.

  • Обратите внимание, что с параметрами сортировки связаны только столбцы таблицы, но не переменные T- SQL:

SELECT @VarcharText = [VarcharText], @NVarcharText = [NVarcharText] FROM [test-sc].[dbo].[UTF8Test] WHERE [Id] = 4 SELECT @VarcharText, Len(@VarcharText), DATALENGTH(@VarcharText), @NVarcharText, Len(@NVarcharText), DATALENGTH(@NVarcharText) SELECT @VarcharText = [VarcharTextSC], @NVarcharText = [NVarcharTextSC] FROM [test-sc].[dbo].[UTF8Test] WHERE [Id] = 4 SELECT @VarcharText, Len(@VarcharText), DATALENGTH(@VarcharText), @NVarcharText, Len(@NVarcharText), DATALENGTH(@NVarcharText) SELECT @VarcharText = [VarcharUTF8], @NVarcharText = [NVarcharUTF8] FROM [test-sc].[dbo].[UTF8Test] WHERE [Id] = 4 SELECT @VarcharText, Len(@VarcharText), DATALENGTH(@VarcharText), @NVarcharText, Len(@NVarcharText), DATALENGTH(@NVarcharText)

SELECT @Variable lengths

0 голосов
/ 20 февраля 2020

Вы можете хранить Unicode в varchar (если хотите), однако каждый байт интерпретируется как один символ, в то время как Unicode (для sql server, utf16, ucs2) использует 2 байта для одного символа, и вы должны учитывайте это при отображении юникода, хранящегося в varchar.

declare @nv nvarchar(10) = N'❤'
select @nv;

declare @v varchar(10) = cast(cast(@nv as varbinary(10)) as varchar(10))
select @v, len(@v); --two chars

select cast(@nv as varbinary(10)), cast(@v as varbinary(10)); --same bytes in both n/var char
--display nchar from char
select cast(cast(@v as varbinary(10)) as nvarchar(10));
0 голосов
/ 20 февраля 2020
declare @char varchar(4)
declare @nvarchar nvarchar(4)

Set @char = '@'
Set @nvarchar = '@'

select @char as charString, 
LEN(@char) as charStringLength,
DATALENGTH(@char) as charStringDataLength

select @nvarchar as nvarcharString, 
LEN(@nvarchar) as nvarcharStringLength,
DATALENGTH(@nvarchar) as nvarcharStringDataLength
0 голосов
/ 20 февраля 2020

Я думал, что оригинальная цитата была немного запутана, поскольку она продолжается

Неправильное представление происходит потому, что при использовании однобайтового кодирования размер хранилища CHAR и VARCHAR равен n байтов и количество символов также n.

, но поскольку в нем упоминается кодировки , я предполагаю, что это утверждение относится к кодировкам UTF, поддерживаемым в SQL Сервер 2019 и выше, который, кажется, позволяет (я еще не пробовал) хранить Unicode в столбцах VARCHAR.

...