不区分大小写的排序规则如何工作?


19

SQL Server中的默认排序规则类型允许对不区分大小写的字符串建立索引,但数据的大小写仍然保留。这实际上如何工作?我正在寻找实际的基本要点,位和字节或详细解释它的好资源。

create table casetest (fruitnames nvarchar(50) not null);
create unique index IX_fruitnames on casetest(fruitnames);

insert into casetest values ('apples');
insert into casetest values ('Pears');
-- this insert fails
insert into casetest values ('pears');

-- this yields 'Pears' as a result
select * from casetest (forceseek) where fruitnames = 'PEARS'

update casetest set fruitnames = 'pears' where fruitnames = 'pEArs'

-- this yields 'pears' as a result
select * from casetest (forceseek) where fruitnames = 'PEARS'

关于SQL Server归类的问题您不太敢问,Robert Sheldon讲述了如何使用归类。它没有涵盖整理的工作原理。我对如何有效地创建/查询索引而不关心案例,同时存储案例数据感兴趣。


1
您可以针对区分大小写的字段有效地查询(例如,利用索引查找)不区分大小写的字符串,但这有点烦人
约翰·埃斯布雷纳

cocogorilla:请参阅注释#1,我刚刚在答案末尾添加了“默认”排序规则。
所罗门·鲁兹基

Answers:


26

针对不区分大小写的字符串建立索引,但数据的大小写仍然保留。这实际上如何工作?

实际上,这不是SQL Server特定的行为,而是这些东西通常的工作方式。

因此,数据就是数据。如果您是专门针对索引,那么数据需要按原样存储,否则每次都需要在主表中进行查找以获得实际值,并且不可能覆盖索引(在至少不适用于字符串类型)。

表/聚集索引或非聚集索引中的数据均不包含任何排序规则/排序信息。它只是数据。排序规则(语言环境/区域性规则和敏感度)只是附加到列的元数据,并在调用排序操作时使用(除非被a覆盖)COLLATE条款),其中包括索引的创建/重建。由非二进制排序规则定义的规则用于生成排序键,排序键是字符串的二进制表示形式(在二进制排序规则中不需要排序键)。这些二进制表示形式结合了所有区域设置/区域性规则和选定的敏感性。排序键用于按正确的顺序放置记录,但它们本身并不存储在索引或表中。它们没有被存储(至少我没有在索引中看到这些值并且被告知它们没有被存储),因为:

  1. 排序并不是真正需要它们,因为它们只是与表或索引中的行的顺序相同。但是,索引的物理顺序只是排序,而不是比较。
  2. 虽然存储它们可能使比较更快,但也会使索引更大,因为单个字符的最小大小为5个字节,这仅仅是“开销”(排序键结构的)。大多数字符每个为2个字节,如果带有重音符,则另加1个字节;如果为大写字母,则另加1个字节。例如,“ e”是一个7字节的密钥,“ E”和“é”都是8字节,而“É”是9字节的密钥。因此,最后不值得存储它们。

归类有两种类型:SQL Server和Windows。

SQL服务器

SQL Server排序规则(那些开始的名字SQL_)是上了年纪,预SQL Server 2000中的排序/比较的方式(即使SQL_Latin1_General_CP1_CI_AS仍然在美国英语操作系统的安装默认情况下,相当可悲)。在这种较旧的,简单化的非Unicode模型中,区域设置,代码页和各种敏感度的每种组合都被赋予了该代码页中每个字符的静态映射。每个字符都分配有一个值(即排序权重)以表示其与其他字符的等同性。此模型中的比较似乎执行两次通过操作:

  1. 首先,它会删除所有重音(例如,“  ü  ”变成“  u  ”),将“ Æ  ” 等字符扩展  为“  A  ”和“  E  ”,然后进行初始排序,以使单词处于自然顺序(如何操作)希望在字典中找到它们)。
  2. 然后,逐个字符地根据每个字符的这些基础值确定相等性。第二部分是mustaccio在他的回答中描述的内容。

在这些归类中唯一可以调整的敏感度是:“大小写”和“重音”(“宽度”,“假名类型”和“变体选择器”不可用)。此外,这些归类均不支持补充字符(这是有意义的,因为它们是Unicode特定的,并且这些归类仅适用于非Unicode数据)。

这种方法适用于对非Unicode VARCHAR数据。语言环境,代码页,区分大小写和区分重音的每个唯一组合都有一个特定的“ sort ID”,您可以在以下示例中看到:

SELECT COLLATIONPROPERTY(N'SQL_Latin1_General_CP1_CI_AS', 'SortID'), -- 52
       COLLATIONPROPERTY(N'SQL_Latin1_General_CP1_CS_AS', 'SortID'), -- 51
       COLLATIONPROPERTY(N'Latin1_General_100_CI_AS',     'SortID'); --  0

前两个排序规则之间的唯一区别是区分大小写。第三个排序规则是Windows排序规则,因此没有静态映射表。

另外,由于归类字符比较简单,这些归类应该比Windows归类更快地进行排序和比较。但是,这些排序规则的功能也要差得多,并且如果可能的话,通常应避免使用它们。

视窗

Windows排序规则(名称不以开头的排序规则SQL_)是较新的(从SQL Server 2000开始)排序/比较的方式。在这种更新的,复杂的Unicode模型中,语言环境,代码页和各种敏感度的每种组合都没有给出静态映射。一方面,此模型中没有代码页。此模型为每个字符分配默认的排序值,然后每个区域设置/区域性都可以将排序值重新分配给任意数量的字符。这允许多种文化以不同的方式使用相同的字符。这样做的效果是,如果不使用相同的字符(并且其中一种不需要重新分配任何值并且可以简单地使用默认值),则可以使用相同的排序规则对多种语言进行自然排序。

此模型中的排序值不是单个值。它们是一个值数组,该值将相对权重分配给基本字母,任何变音符号(即重音符号),大写字母等。如果排序规则区分大小写,则使用该数组的“ case”部分,否则将被忽略(因此,不敏感)。如果排序规则对重音敏感,则使用数组的“变音符号”部分,否则将忽略该字符串(因此,不敏感)。

此模型中的比较是多遍操作:

  1. 首先,对字符串进行规范化,以便表示同一个字符的各种方式等同。例如,“ ü ”可以是单个字符/代码点(U + 00FC)。您也可以将非重音符号“ u ”(U + 0075)与组合音调“ ̈ ”(U + 0308)组合在一起,以得到:“ ü ”,它不仅在渲染时看起来相同(除非存在问题)。您的字体),但也被认为与单字符版本(U + 00FC)相同,除非使用二进制排序规则(比较字节而不是字符)。规范化将单个字符分为多个部分,其中包括对诸如“ Æ  ”之类的字符的扩展  (如上文针对SQL Server归类所述)。
  2. 该模型中的比较操作针对每个灵敏度逐个字符地进行。字符串的排序键是通过应用敏感度为“敏感”的每个字符整理值数组的适当元素来确定的。排序键值的排列方式是,每个字符(基本字符)的所有主要敏感度,然后是所有次要敏感度(变音符号),然后是每个字符的大小写,依此类推。
  3. 根据计算出的排序键执行排序。将每个敏感度分组在一起时,比较多个字符的字符串时,您可以获得与等效SQL Server排序规则不同的排序顺序,并且涉及重音,并且该排序规则是重音敏感的(如果排序规则为也区分大小写)。

有关此排序的更多详细信息,我最终将发布一篇文章,其中显示排序键值,它们的计算方式,SQL Server和Windows排序规则之间的差异等。但是,现在,请参阅我对以下内容的回答:重音敏感排序(请注意,该问题的另一个答案是对官方Unicode算法的很好的解释,但是SQL Server而是使用自定义(尽管相似)算法,甚至是自定义权重表)。

可以在以下归类中调整所有敏感度:“大小写”,“重音”,“宽度”,“假名类型”和“变量选择器”(从SQL Server 2017开始,仅适用于日语归类)。此外,其中一些归类(当与Unicode数据一起使用时)支持补充字符(从SQL Server 2012开始)。此方法适用于NVARCHAR VARCHAR数据(甚至是非Unicode数据)。VARCHAR通过首先在内部将值转换为Unicode,然后应用排序/比较规则,它可应用于非Unicode 数据。


请注意:

  1. 没有SQL Server的通用默认排序规则。根据安装时操作系统的当前语言环境/语言设置,有一个默认的安装默认设置(不幸SQL_Latin1_General_CP1_CI_AS的是,对于美国英语系统,请对该建议投赞成票)。可以在安装过程中更改。然后,此实例级别的排序规则将为[model]数据库设置排序规则,该排序规则是创建新DB时使用的模板,但是在执行时可以CREATE DATABASE通过指定COLLATE子句来更改排序规则。当COLLATE未指定该子句时,此数据库级排序规则用于变量和字符串文字,以及新(和更改!)列的默认排序规则(问题中的示例代码就是这种情况)。
  2. 有关归类/编码/ Unicode的更多信息,请访问:归类信息

5

通常,这是通过使用归类表为每个字符分配一定的分数来实现的。排序例程具有一个比较器,该比较器使用适当的表(默认值或显式指定的表)来使用其归类分数逐个字符地比较字符串。例如,如果特定的归类表为“ a”分配了1的分数,为“ A”分配了201的分数,并且在此特定实现中较低的分数意味着较高的优先级,则“ a”将在“ A”之前进行排序。另一个表可能会分配反向分数:201分配给“ a”,1分配给“ A”,排序顺序随后将反向。另一个表可能会为“ a”,“ A”,“Á”和“Å”分配相等的分数,这将导致不区分大小写和变音的比较和排序。

类似地,当将索引键与谓词中提供的值进行比较时,会使用这种基于排序规则表的比较器。


1
仅供参考:此信息仅在数据上使用SQL Server排序规则(即名称以开头的排序规则SQL_)时才是正确的VARCHAR。对于使用Windows排序规则(名称不以开头)的NVARCHAR数据或VARCHAR数据,这并非完全正确。SQL_
所罗门·鲁兹基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.