选择一个二进制排序规则,可以区分Sql Server中{n = 44'的nvarchar列的'ss'和'ß'

时间:2017-03-18 08:02:52

标签: sql-server tsql unicode collation sql-server-2016

由于SQL Server的默认SQL_Latin1_General_CP1_CI_AS排序规则无法区分ssß,因此我想将表中特定列的排序规则更改为{{1} },如here中所述。

但是,我不确定这是否通常是一种好习惯。此外,我不确定除以下内容之外的其他影响:

  • 更改排序顺序:由于我从不对此列上的数据进行排序,因此对我来说可能不是问题。但是,如果您不这么认为,请告诉我。
  • 更改不区分大小写的区分大小写:由于我的应用程序始终以小写形式提供文本,因此我认为此更改对我来说也不是问题。但是,如果您不这么认为,请告诉我。

我很好奇这个变化的其他主要影响,如果有的话。

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

  

SQL_Latin1_General_CP437_BIN

     

描述:Latin1-常规,Unicode数据的二进制排序,SQL Server在代码页437上排序,用于非Unicode数据

           

SQL_Latin1_General_CP437_BIN2

     

描述:Latin1-常规,Unicode数据的二进制代码点比较排序,SQL Server在代码页437上的排序顺序30用于非Unicode数据

           

SQL_Latin1_General_CP850_BIN

     

描述:Latin1-常规,Unicode数据的二进制排序,SQL Server在代码页850上的排序顺序40,用于非Unicode数据

           

SQL_Latin1_General_CP850_BIN2

     

描述:Latin1-常规,Unicode数据的二进制代码点比较排序,SQL Server在代码页850上的排序顺序40,用于非Unicode数据

如果您认为其他排序规则更适合此方案,请同时提及这些排序规则。

2017年3月19日更新: 对于提出这个问题的任何人:

  • 必须检查来自@srutzky和@SqlZim的答案,以及相关的引用资源。在这种情况下,你不想急于做事。
  • 由于改变校对不适合胆小鬼:P,保留表数据的备份可能会派上用场。
  • 还要检查列的依赖关系,例如索引和约束;你可能需要放弃并创建它们,就像我的情况一样。

玩得开心:)

2 个答案:

答案 0 :(得分:16)

关于Collat​​ions的一些事情:

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

    SQL_排序规则的问题实际上只与VARCHAR(即非Unicode)数据有关,因为NVARCHAR(即Unicode)数据使用操作系统中的规则。但遗憾的是,VARCHAR数据的排序和比较规则使用了简单的映射,并没有包含更复杂的语言规则。这就是ssß使用相同的VARCHAR排序规则存储为SQL_Latin1_General_CP1_CI_AS时不等同的原因。当在单词的中间使用时,这些已弃用的Collat​​ions也无法给破折号赋予较低的权重。非SQL_排序规则(即Windows排序规则)对VARCHARNVARCHAR使用相同的规则,因此VARCHAR处理更加健壮,与{{1}更加一致}。

  2. 从SQL Server 2005开始,NVARCHAR排序已弃用。如果您可以避免使用它们,那么您应该(但这并不意味着如果没有迫切需要改变一堆事情) !)。

    _BIN排序规则的问题相当微妙,因为它只影响排序。 _BIN_BIN归类之间的比较是相同的,因为它们在字节级别进行比较(因此没有语言规则)。但是,由于SQL Server(和Windows / PC)是Little Endian,实体以反向字节顺序存储。这在处理双字节“字符”时变得明显,这是_BIN2数据:UTF-16 Little Endian。这意味着Unicode代码点U + 1216在Big Endian系统上具有0x1216的十六进制/二进制表示,但在Little Endian系统上存储为0x1612。要完全循环,以便最后一点的重要性(希望)变得明显:NVARCHAR Collat​​ions将逐字节比较(在第一个字符之后),因此将U + 1216视为0x16然后是0x12,而_BIN排序将按代码点比较代码点,因此将U + 1216视为0x12,然后是0x16。

  3. 此特定列为_BIN2(使用NVARCHAR的{​​{1}}列不等同于VARCHARSQL_Latin1_General_CP1_CI_AS),因此仅适用于此列单独,ssß之间没有区别,因为Unicode是一个单一的全包字符集。

  4. 对于SQL_Latin1_General_CP437_BIN2数据,会有不同之处,因为它们是不同的代码页(437850),而且这两者都不同于您正在使用(SQL_Latin1_General_CP850_BIN2 ==代码页1252)。

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

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

    A
  6. 所以:

    • 如果您要使用二进制排序规则,请使用
    • 之类的内容
    • 需要更改整个数据库及其所有表的排序规则。那是很多的工作,唯一的“内置”机制是没有文档记录(即不受支持)。
    • 如果要更改数据库的默认排序规则,则会影响数据库范围的项目(如表,列,索引,函数,存储过程等)的名称解析。含义:您需要回归100%的应用程序触摸数据库,以及触摸此数据库的所有SQL Server代理作业等。
    • 如果使用此列的大多数/所有查询都需要将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视为不同,请继续并更改列以使用ß。这可能需要删除以下依赖对象,然后在ss

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

      提示:请务必检查列的当前NULL / NOT NULL设置,并在Latin1_General_100_BIN2语句中指定它,以便它不会被更改。

      < / LI>
    • 如果只有一些查询需要这种不同的行为,那么在每个条件的基础上(例如ALTER TABLE)仅覆盖那些具有ALTER TABLE ... ALTER COLUMN ...子句的比较操作。只应在运营商的一方需要COLLATE关键字,因为排序优先顺序会将其应用到另一方。

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

答案 1 :(得分:6)

通常,BIN2优于BIN,您可能希望在sql排序规则上选择Windows排序规则。例如Latin1_General_100_BIN2

Guidelines for Using BIN and BIN2 Collations

  

使用BIN归类的指南

     

如果您的SQL Server应用程序与使用二进制排序规则的旧版SQL Server交互,请继续使用二进制文件。对于混合环境,二进制排序可能是更合适的选择。

  

出于与 BIN2排序规则有关的类似原因,除非您对维护向后兼容性行为有特定要求,否则您应该倾向于使用Windows排序规则而不是SQL Server特定的排序规则(即以SQL 开头的那些现在被认为是有点&#34; sucky&#34 ;;-))。   
- @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;

返回:

+----+---+----+---+-----+-----+-----+-----+
| 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;

返回:

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