字符串的排序规则和数据类型不兼容

Collation and datatype incompatibility on strings

我对系统在涉及排序规则和数据类型差异的情况下的行为感到非常困惑。

作为一个最小的例子,我将相同的 Unicode 值输入到两个不同 table 的单列中。在一个 table 中,列是 varchar 并且属于某种排序规则,而在另一列中是 nvarchar 并且属于另一种排序规则。代码和结果:

create table cn(code nvarchar(max) collate Latin1_General_CI_AS)
create table cv(code varchar(max) collate SQL_Latin1_General_CP1253_CI_AI)

insert cn select N'3VT18021δ'
insert cv select N'3VT18021δ'

select * from cn
select * from cv

--1. 
select * from cn inner join cv on cn.code=cv.code 
-- Cannot resolve the collation conflict between "SQL_Latin1_General_CP1253_CI_AI" and "Latin1_General_CI_AS" in the equal to operation.

--2. 
select * from cn inner join cv on cn.code=cv.code collate SQL_Latin1_General_CP1253_CI_AI   
-- returns one row

--3. 
select * from cn inner join cv on cn.code =cv.code collate Latin1_General_CI_AS 
-- returns 0 rows

--4. 
select * from cn inner join cv on cn.code collate SQL_Latin1_General_CP1253_CI_AI =cv.code   
-- returns one row

--5. 
select * from cn inner join cv on cn.code collate Latin1_General_CI_AS =cv.code 
-- returns one row

我的笔记:

Case 1: collation difference, I understand

Cases 2 and 5: return (correctly) one row. Why does collating a field to its own collation do any good?

Cases 3 and 4: Why converting one's collation to the other works one time, but not the other?

当然,所有这些都因数据类型的差异而变得更加复杂。

Cases 2 and 5: return (correctly) one row. Why does collating a field to its own collation do any good?

当您在子句 的值上显式使用 COLLATE 时,表达式的两边 都显式转换为该排序规则,因此没有冲突。

Cases 3 and 4: Why converting one's collation to the other works one time, but not the other?

您的其中一列是 varchar,因此当它从一种排序规则更改为另一种排序规则时,它的值也会发生变化。具体来说,当您 COLLATE 将 table cv 中的值添加到排序规则 Latin1_General_CI_AS 时。由于 'δ' 不是 varchar 的排序规则中可用的字符,它变为 'd' 并且 '3VT18021d' 不是 等于 N'3VT18021δ'。您可以通过以下方式查看:

SELECT code COLLATE Latin1_General_CI_AS
FROM cv;

您需要首先将该值显式转换为 nvarchar

select *
from cn
     inner join cv on cn.code = CONVERT(nvarchar(MAX),cv.code) collate Latin1_General_CI_AS;
--Returns one row now

编辑:解释为什么查询 3 没有 return 数据,而查询 5 有,这是因为 COLLATE 的定位以及何时发生隐式转换。

cn.code =cv.code collate Latin1_General_CI_AS --3
cn.code collate Latin1_General_CI_AS =cv.code --5

对于查询 3,COLLATE 表达式位于 cv.code,即 varchar。结果,值的排序规则更改为 first,字符 'δ' 丢失。然后根据数据类型优先级将其隐式转换为 nvarchar

然而,对于查询 5,COLLATEcn.codenvarchar。因此,当更改值的排序规则时,不会丢失任何字符。由于 cv.code 没有明确的 COLLATE,而是先将其转换为 nvarchar(由于数据类型优先),然后 然后 整理;不会造成字符丢失。

排序规则是数据类型的一部分。如果您使用不同的排序规则,字符的内部表示可能会有所不同,并且许多约束在使用不同的排序规则(PRIMARY KEY、UNIQUE、CHECK...)时没有相同的行为。

在运算符(=、LIKE、+)和某些函数(CONCAT...)中混合使用不同的排序规则会系统地导致错误,直到您为此操作施加特定的排序规则。 所以有一个 COLLATE 关键字充当运算符来消除可以使用哪种排序规则的歧义。

SQL服务器区分两种排序规则。

  1. 名称以 SQL_
  2. 开头的技术整理
  3. 出于功能目的的语义整理,名称以语言名称开头

技术归类 只能用于恢复具有特定编码的导入数据...例如,您可以使用严格等同于 IBM EBCDIC 的归类,但是为 SQL 服务器表操作保留此排序规则将是一个愚蠢的想法!

语义归类 广泛用于促进应用程序功能...您想要 CI 还是 CS(大小写行为)、AI 或 AS(变音行为) , WS(宽行为,如 2 = ²),等等...

使用这个查询:

select CAST(code AS VARBINARY(max)) from cn;
select CAST(code AS VARBINARY(max)) from cv;

你会发现最后一个字符没有相同的代码。这就是为什么在使用 Latin1_General_CI_AS 排序规则时结果没有行...

您将看到 NVARCHAR(max) 数据类型的“B403”字符编码为 2 个字节,无法转换为每个字符 1 个字节的 PAGE CODE CP1253...

实际上 SQL_Latin1_General_CP1253_CI_AI 的 VARCHAR 中的 B4 字节是“ä”而不是“δ”

换句话说,尝试将 1 个字节放入 2 个字节中很容易...只需添加一些零即可。但是,相反,只有当右边的字节为零时,才可能尝试将 2 个字节放在一个中...