为 Sql 服务器中的 nvarchar 列选择可以区分 'ss' 和 'ß' 的二进制排序规则

Choosing a binary collation that can differentiate between 'ss' and 'ß' for nvarchar column in Sql Server

由于 SQL 服务器的默认 SQL_Latin1_General_CP1_CI_AS 排序规则无法区分 ssß,我想更改一个特定列的排序规则table 到 SQL_Latin1_General_CP437_BIN2,如 here.

中所建议

但是,我不确定这通常是不是一个好习惯。此外,我不确定以下内容以外的含义:

我很好奇此更改的其他主要含义(如果有的话)。

此外,我还想知道以下哪一项最适合这种情况:

SQL_Latin1_General_CP437_BIN

Description: Latin1-General, binary sort for Unicode Data, SQL Server Sort Order 30 on Code Page 437 for non-Unicode Data


SQL_Latin1_General_CP437_BIN2

Description: Latin1-General, binary code point comparison sort for Unicode Data, SQL Server Sort Order 30 on Code Page 437 for non-Unicode Data


SQL_Latin1_General_CP850_BIN

Description: Latin1-General, binary sort for Unicode Data, SQL Server Sort Order 40 on Code Page 850 for non-Unicode Data


SQL_Latin1_General_CP850_BIN2

Description: Latin1-General, binary code point comparison sort for Unicode Data, SQL Server Sort Order 40 on Code Page 850 for non-Unicode Data

如果您认为还有其他排序规则更适合这种情况,请一并提及。


2017 年 3 月 19 日更新: 对于任何来回答这个问题的人:

玩得开心:)

一般来说,BIN2 优于 BIN,您可能希望选择 windows 排序规则而不是 sql 排序规则。例如Latin1_General_100_BIN2

Guidelines for Using BIN and BIN2 Collations

Guidelines for Using BIN Collations

If your SQL Server applications interact with older versions of SQL Server that use binary collations, continue to use binary. Binary collations might be a more suitable choice for mixed environments.


For similar reasons to what has just been stated regarding the BIN2 collations, unless you have specific requirements to maintain backwards-compatibility behavior, you should lean towards using the Windows collations and not the SQL Server-specific collations (i.e. the ones starting with SQL are now considered kinda "sucky" ;-) ).
- @srutzky - Latin1_General_BIN performance impact when changing the database default collation


rextester 演示:http://rextester.com/KIIDYH74471

create table t (
    a varchar(16)  --collate SQL_Latin1_General_CP1_CI_AS /* default */
  , b varchar(16)  --collate SQL_Latin1_General_CP1_CI_AS
  , c nvarchar(16) --collate SQL_Latin1_General_CP1_CI_AS
  , d nvarchar(16) --collate SQL_Latin1_General_CP1_CI_AS 
);
insert into t values ('ss','ß',N'ss',N'ß');
select *
    , case when a = b then '=' else '!=' end as [a=b] /* != */
    , case when a = d then '=' else '!=' end as [a=d] /* = */
    , case when c = b then '=' else '!=' end as [c=b] /* = */
    , case when c = d then '=' else '!=' end as [c=d] /* = */
from t;

returns:

+----+---+----+---+-----+-----+-----+-----+
| a  | b | c  | d | a=b | a=d | c=b | c=d |
+----+---+----+---+-----+-----+-----+-----+
| ss | ß | ss | ß | !=  | =   | =   | =   |
+----+---+----+---+-----+-----+-----+-----+

create table t (
    a varchar(16)  collate Latin1_General_100_BIN2
  , b varchar(16)  collate Latin1_General_100_BIN2
  , c nvarchar(16) collate Latin1_General_100_BIN2
  , d nvarchar(16) collate Latin1_General_100_BIN2
);
insert into t values ('ss','ß',N'ss',N'ß');
select *
    , case when a = b then '=' else '!=' end as [a=b] /* != */
    , case when a = d then '=' else '!=' end as [a=d] /* != */
    , case when c = b then '=' else '!=' end as [c=b] /* != */
    , case when c = d then '=' else '!=' end as [c=d] /* != */
from t;

returns:

+----+---+----+---+-----+-----+-----+-----+
| a  | b | c  | d | a=b | a=d | c=b | c=d |
+----+---+----+---+-----+-----+-----+-----+
| ss | ß | ss | ß | !=  | !=  | !=  | !=  |
+----+---+----+---+-----+-----+-----+-----+

关于排序规则的一些事情:

  1. 从 SQL Server 2000(是的,2000)开始不推荐使用 SQL_ 排序规则。如果你可以避免使用它们,你应该(但这并不意味着如果没有迫切需要就去改变一堆东西!)。

    SQL_ 排序规则的问题实际上只与 VARCHAR(即非 Unicode)数据有关,因为 NVARCHAR(即 Unicode)数据使用来自 OS。但不幸的是,VARCHAR 数据的排序和比较规则使用简单的映射,不包括更复杂的语言规则。这就是为什么 ssß 在使用相同的 SQL_Latin1_General_CP1_CI_AS 排序规则存储为 VARCHAR 时不等同的原因。当在单词中间使用时,这些已弃用的排序规则也无法降低破折号的权重。非 SQL_ 排序规则(即 Windows 排序规则)对 VARCHARNVARCHAR 使用相同的规则,因此 VARCHAR 处理更健壮,更符合NVARCHAR.

  2. 从 SQL Server 2005 开始不推荐使用 _BIN 排序规则。如果可以避免使用它们,您应该(但这并不意味着去改变一堆如果没有迫切需要的话!)。

    _BIN 归类的问题相当微妙,因为它只影响排序。 _BIN_BIN2 排序规则之间的比较是相同的,因为它们是在字节级别进行比较的(因此没有语言规则)。但是,由于 SQL 服务器(和 Windows / PC)是 Little Endian,实体以反向字节顺序存储。这在处理双字节 "characters" 时变得很明显,这就是 NVARCHAR 数据:UTF-16 Little Endian。这意味着 Unicode 代码点 U+1216 在 Big Endian 系统上具有 0x1216 的 hex/binary 表示,但在 Little Endian 系统上存储为 0x1612。回到原点,最后一点的重要性将(希望)变得显而易见:_BIN 归类将逐字节比较(在第一个字符之后),因此将 U+1216 视为 0x16,然后是 0x12,而 _BIN2 排序规则将逐个代码点比较代码点,因此将 U+1216 视为 0x12,然后是 0x16。

  3. 这个特定的列是 NVARCHAR(使用 SQL_Latin1_General_CP1_CI_ASVARCHAR 列不等同于 ssß)等等就这一列而言,SQL_Latin1_General_CP437_BIN2SQL_Latin1_General_CP850_BIN2 之间没有区别,因为 Unicode 是一个单一的、包罗万象的字符集。

  4. 对于VARCHAR数据,会有不同,因为它们是不同的代码页(437 and 850), and both of those are different than the one that you are using now (CP1 == code page 1252)。

  5. 虽然使用二进制排序规则通常是矫枉过正,但在这种情况下可能有必要,因为只有一种语言环境/文化不等同于 ßss : 匈牙利语。使用 Hungarian Collat​​ion 可能有一些您不想要(或至少不期望)的语言规则,因此二进制 Collat​​ion 似乎是这里更好的选择(只是不是您询问的 4 个中的任何一个 :-) .请记住,通过使用二进制排序规则,您不仅放弃了 所有 语言规则,而且还失去了将同一字符的不同版本等同起来的能力,例如 A (Latin Capital Letter A U+0041) and (Fullwidth Latin Capital Letter A U+FF21).

    使用以下查询查看哪些排序规则是非二进制的并且不等同于这些字符:

    DECLARE @SQL NVARCHAR(MAX) = N'DECLARE @Counter INT = 1;';
    
    SELECT @SQL += REPLACE(N'
      IF(N''ß'' COLLATE {Name} <> N''ss'' COLLATE {Name})
      BEGIN
        RAISERROR(N''%4d.  {Name}'', 10, 1, @Counter) WITH NOWAIT;
        SET @Counter += 1;
      END;
    ', N'{Name}', col.[name]) + NCHAR(13) + NCHAR(10)
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] NOT LIKE N'SQL[_]%'
    AND    col.[name] NOT LIKE N'%[_]BIN%'
    ORDER BY col.[name]
    
    --PRINT @SQL;
    EXEC (@SQL);
    

所以:

  • 如果您要使用二进制排序规则,请使用类似 Latin1_General_100_BIN2 的东西。
  • 不需要更改整个数据库及其所有表的排序规则。那是 很多 的工作,唯一的 "built-in" 机制没有记录(即不受支持)。
  • 如果您要更改数据库的默认排序规则,这会影响数据库范围内项目(例如表、列、索引、函数、存储过程等)的名称解析。这意味着:您需要回归 100%接触数据库的应用程序,以及接触该数据库的所有 SQL 服务器代理作业等。
  • 如果大多数/所有使用此列的查询需要 ßss 被视为不同,那么继续更改该列以使用 Latin1_General_100_BIN2。这可能需要删除以下依赖对象,然后在 ALTER TABLE:

    之后重新创建
    • 索引
    • 唯一约束
    • 外键约束

    提示: 务必检查列的当前 NULL/NOT NULL 设置,并在 ALTER TABLE ... ALTER COLUMN ... 语句中指定,以免更改。

  • 如果只有一些查询需要这种不同的行为,则在每个条件的基础上(例如 WHERE tab.[ThisColumn] LIKE N'%ss%' COLLATE Latin1_General_100_BIN2),使用 COLLATE 子句重写那些比较操作。 COLLATE 关键字只应在(运算符的)一侧需要,因为排序规则优先级会将其应用于另一侧。

有关使用字符串和排序规则的更多信息,请访问:Collations Info