ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

UTF-8支持对SQL Server 2019的影响

2020-03-03 22:05:54  阅读:620  来源: 互联网

标签:UTF UTF8 50 Server coll 2019 SC SELECT


概述

  SQL Server长期以来一直以nchar,nvarchar和ntext数据类型的形式支持Unicode字符,这些字符仅限于UTF-16。可以将UTF-8数据放入nchar和nvarchar列中,但这 通常很乏味,即使 在SQL Server 2014 SP2中添加了通过BCP和BULK INSERT的UTF-8支持之后。最终结果是要支付Unicode的存储和内存需求,因为即使部分或全部为ASCII,仍然必须将所有数据存储为Unicode。

  在SQL Server 2019中,有新的UTF-8排序规则,可让您节省存储空间,同时仍可享受兼容性和原生存储UTF-8数据的好处。与Unicode压缩类似(但不完全相同),您只需为实际需要该字符的字符支付额外的存储空间。但是,实际的存储影响是什么?这如何影响内存授予和查询性能?

解决方案

  各种归类,代码页和UTF格式都有许多含义。我觉得专家可以撰写20个部分的文章系列,但仍然没有完成。实际上,所罗门·鲁兹基(Solomon Rutzky)已经写了很多有关这些主题的文章,最近 一篇有关SQL Server 2019中对UTF-8支持的文章 –这表明您可能不应该使用此功能,并且您应该专注于使用UTF-8列的排序规则应该主要是关于兼容性,而不是存储空间或性能。

  因为我知道尽管有 Solomon的建议,人们仍然会使用它,所以我只想专注于特定的UTF-8排序规则,以及与传统Unicode列中存储的UTF-16数据相比,空间和内存要求有何不同。我将比较压缩与不压缩以及列值的各种百分比(以及表中行的百分比)与非ASCII数据的比较。

   首先,让我们看一下一个表,该表包含具有三个不同排序规则的列,并查看当我们向其中插入数据时的外观。我为该查询拍摄了屏幕截图,因为我知道其中一些Unicode字符在到达您的设备时无法很好地转换:

共有三列,第一列使用标准Latin1_General归类,第二列包含具有补充字符(SC)的Latin1_General,第三列使用新的Latin1_General UTF-8归类。我分别插入了希腊字符,亚洲字符和表情符号(当然是加拿大国旗!),然后再插入一些其他ASCII字符。这是每个值的LEN()和DATALENGTH()的结果:

   显然,您可以看到长度基本相同,唯一的不同是表情符号在第一次排序时需要四个字节(请参阅 Greg Low的这篇文章,以了解为什么这是字节而不是字符)。但是,使用UTF-8归类时,实际存储几乎总是相同或更低(再次,除了一个例外,这次亚洲字符需要一个额外的字节)。我为您省去了一个悬念:通过行和页面压缩以及类似的#temp表,所有结果都是相同的。

另外,上面的代码示例中的注释表明,即使目标类型是varchar,您仍然需要在字符串文字上使用N前缀。原因是SQL Server将首先尝试解释字符串的值,如果N不存在,则部分Unicode数据会丢失。

尝试这个:

DECLARE @t TABLE (t varchar(10) COLLATE Latin1_General_100_CI_AI_SC_UTF8);
INSERT @t(t) VALUES('h'),(N'h');
SELECT t FROM @t;
t
----
?
h

在玩这个游戏的过程中,我还发现了另一种现象,可能与排序规则完全无关,但仍然很有趣。当使用Unicode字符串的varbinary表示形式时(例如一堆poo表情符号,0x3DD8A9DC),可以根据语句中的其他内容来不同地解释它们。在此示例中,我要执行三个不同的批处理:

(1)直接插入varbinary值;

(2)直接插入值,并在单独的语句中,将值转换为nvarchar后插入;

(3)将值和转换后的值插入同一条语句中:

DECLARE @t TABLE (t varchar(10) COLLATE Latin1_General_100_CI_AI_SC_UTF8);
INSERT @t(t) VALUES(0x3DD8A9DC);
SELECT t FROM @t;
GO -- 1
 
DECLARE @t TABLE (t varchar(10) COLLATE Latin1_General_100_CI_AI_SC_UTF8);
INSERT @t(t) VALUES(0x3DD8A9DC);
INSERT @t(t) VALUES(CONVERT(nvarchar(10),0x3DD8A9DC));
SELECT t FROM @t;
GO -- 2
 
DECLARE @t TABLE (t varchar(10) COLLATE Latin1_General_100_CI_AI_SC_UTF8);
INSERT @t(t) VALUES(0x3DD8A9DC),(CONVERT(nvarchar(10),0x3DD8A9DC));
SELECT t FROM @t;
GO -- 3

结果让我感到困惑:

在使用不同的语句执行插入的情况下,两个解释都正确。但是,当使用VALUES()将两行插入在一起时,两者都以某种方式转换为nvarchar。涉及VALUES()的行为,可能与归类无关,但在以后的技巧中,我将不得不对此进行研究。同时,如果要将脚本从一种形式更改为另一种形式,请注意这一点。

回到原来的调查;如果我们大规模尝试该怎么办?我编写了一个脚本,该脚本为一堆表生成CREATE TABLE语句,这些表具有用于校对,压缩和实际存储多少非ASCII数据的各种设置。具体来说,这将创建81个表,这些表具有以下组合:

  • 压缩(行,页,无);
  • 排序规则(Latin1_General_100_CI_AI,Latin1_General_100_CI_AI _SC和Latin1_General_100_CI_AI _SC_UTF8);
  • 包含UTF-8数据的行百分比(0%,50%,100%);和,
  • 每行是UTF-8数据的字符数(0个字符,25个字符和50个字符):
CREATE TABLE #cmp(cmp varchar(4));
INSERT #cmp VALUES('ROW'),('PAGE'),('NONE');
CREATE TABLE #coll(coll varchar(8));
INSERT #coll VALUES(''),('_SC'),('_SC_UTF8');
CREATE TABLE #row(rowconf varchar(9));
INSERT #row VALUES('0  % UTF8'),('50 % UTF8'),('100% UTF8');
CREATE TABLE #char(charconf varchar(7));
INSERT #char VALUES('0 UTF8'),('25 UTF8'),('50 UTF8');
SELECT N'CREATE TABLE dbo.' + QUOTENAME(N'UTF8Test' + coll.coll + N'_' 
  + cmp.cmp + N'_' + rowconf + N'_' + charconf) + N'
(
    id int IDENTITY(1,1) NOT NULL,
    the_column ' + CASE coll.coll WHEN '_SC_UTF8' THEN N'' ELSE N'n' END + N'varchar(512)' END 
    + N' COLLATE Latin1_General_100_CI_AI' + coll.coll + N',
    CONSTRAINT ' + QUOTENAME(N'pk_UTF8Test_' + coll.coll + N'_' + cmp.cmp 
    + N'_' + rowconf + N'_' + charconf) + N' PRIMARY KEY CLUSTERED(id) 
    WITH (DATA_COMPRESSION = ' + cmp.cmp + N')
);' FROM #cmp AS cmp, #coll AS coll, #row AS rowconf, #char AS charconf;

该脚本产生81行输出,并具有如下表定义(当然,它们不是漂亮的脚本):

CREATE TABLE dbo.[UTF8Test_ROW_0  % UTF8_0 UTF8]
(
    id int IDENTITY(1,1) NOT NULL,
    the_column nvarchar(200) COLLATE Latin1_General_100_CI_AI,
    CONSTRAINT [pk_UTF8Test__ROW_0  % UTF8_0 UTF8] PRIMARY KEY CLUSTERED(id)
      WITH (DATA_COMPRESSION = ROW)
);
CREATE TABLE dbo.[UTF8Test_SC_ROW_0  % UTF8_0 UTF8]
(
    id int IDENTITY(1,1) NOT NULL,
    the_column nvarchar(200) COLLATE Latin1_General_100_CI_AI_SC,
    CONSTRAINT [pk_UTF8Test__SC_ROW_0  % UTF8_0 UTF8] PRIMARY KEY CLUSTERED(id)
      WITH (DATA_COMPRESSION = ROW)
);
CREATE TABLE dbo.[UTF8Test_SC_UTF8_ROW_0  % UTF8_0 UTF8]
(
    id int IDENTITY(1,1) NOT NULL,
    the_column varchar(200) COLLATE Latin1_General_100_CI_AI_SC_UTF8,
    CONSTRAINT [pk_UTF8Test__SC_UTF8_ROW_0  % UTF8_0 UTF8] PRIMARY KEY CLUSTERED(id)
      WITH (DATA_COMPRESSION = ROW)
);

… 78 more tables …

 

复制,粘贴,执行,现在您有81个表,可以生成INSERT语句以类似的方式进行填充。这里涉及更多逻辑,因此脚本更加丑陋-我们希望在每个表中插入10,000行,但是这些行是部分或全部填充(或未填充)Unicode数据的值的混合。我在这里有加拿大国旗,并在该位置添加了注释,以防它无法在您的浏览器中正确显示:

DECLARE @sql nvarchar(max) = N'SET NOCOUNT ON;';
SELECT @sql += N'
WITH n AS (SELECT n = 1 UNION ALL SELECT n+1 FROM n WHERE n < 10000)
INSERT dbo.' + QUOTENAME(N'UTF8Test' + coll.coll + N'_' + cmp.cmp 
  + N'_' + rowconf + N'_' + charconf) + N'(the_column) SELECT b FROM (SELECT 
  b = REPLICATE(N''

标签:UTF,UTF8,50,Server,coll,2019,SC,SELECT
来源: https://blog.csdn.net/ren6370/article/details/104636654

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有