首页 > 解决方案 > varchar 中用于 Unicode 字符的字节数

问题描述

一个常见的误解是认为 CHAR(n) 和 VARCHAR(n) 中的 n 定义了字符数。但在 CHAR(n) 和 VARCHAR(n) 中,n 定义了以字节为单位的字符串长度 (0-8,000)。n 从不定义可以存储的字符数

根据微软的这个声明,我假设 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

查询结果

有人可以解释其背后的原因吗?

标签: sql-servervarcharsqldatatypesnvarchar

解决方案


抽出时间来测试我的第一个答案的假设:

  • 创建支持 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]

选择长度

我惊讶地发现,VARCHAR在使用 UTF8 排序规则时,需要修改“a 只存储单字节字符”这一古老的口头禅。

  • 请注意,只有表列与排序规则相关联,而不是 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 @可变长度


推荐阅读