为 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 日更新:
对于任何来回答这个问题的人:
- 必须检查@srutzky 和@SqlZim 的答案,以及相关的引用资源。在这种情况下你不想仓促行事。
- 由于更改排序规则不适合胆小的人:P,保留 table 数据的备份可能会派上用场。
- 还要检查column的依赖,比如index和constraint;您可能需要删除并创建它们,就像我的情况一样。
玩得开心:)
一般来说,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 | ß | != | != | != | != |
+----+---+----+---+-----+-----+-----+-----+
关于排序规则的一些事情:
从 SQL Server 2000(是的,2000)开始不推荐使用 SQL_
排序规则。如果你可以避免使用它们,你应该(但这并不意味着如果没有迫切需要就去改变一堆东西!)。
SQL_
排序规则的问题实际上只与 VARCHAR
(即非 Unicode)数据有关,因为 NVARCHAR
(即 Unicode)数据使用来自 OS。但不幸的是,VARCHAR
数据的排序和比较规则使用简单的映射,不包括更复杂的语言规则。这就是为什么 ss
和 ß
在使用相同的 SQL_Latin1_General_CP1_CI_AS
排序规则存储为 VARCHAR
时不等同的原因。当在单词中间使用时,这些已弃用的排序规则也无法降低破折号的权重。非 SQL_
排序规则(即 Windows 排序规则)对 VARCHAR
和 NVARCHAR
使用相同的规则,因此 VARCHAR
处理更健壮,更符合NVARCHAR
.
从 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。
这个特定的列是 NVARCHAR
(使用 SQL_Latin1_General_CP1_CI_AS
的 VARCHAR
列不等同于 ss
和 ß
)等等就这一列而言,SQL_Latin1_General_CP437_BIN2
和 SQL_Latin1_General_CP850_BIN2
之间没有区别,因为 Unicode 是一个单一的、包罗万象的字符集。
对于VARCHAR
数据,会有不同,因为它们是不同的代码页(437 and 850), and both of those are different than the one that you are using now (CP1
== code page 1252)。
虽然使用二进制排序规则通常是矫枉过正,但在这种情况下可能有必要,因为只有一种语言环境/文化不等同于 ß
与 ss
: 匈牙利语。使用 Hungarian Collation 可能有一些您不想要(或至少不期望)的语言规则,因此二进制 Collation 似乎是这里更好的选择(只是不是您询问的 4 个中的任何一个 :-) .请记住,通过使用二进制排序规则,您不仅放弃了 所有 语言规则,而且还失去了将同一字符的不同版本等同起来的能力,例如 A
(Latin Capital Letter A U+0041) and A
(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
由于 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 日更新: 对于任何来回答这个问题的人:
- 必须检查@srutzky 和@SqlZim 的答案,以及相关的引用资源。在这种情况下你不想仓促行事。
- 由于更改排序规则不适合胆小的人:P,保留 table 数据的备份可能会派上用场。
- 还要检查column的依赖,比如index和constraint;您可能需要删除并创建它们,就像我的情况一样。
玩得开心:)
一般来说,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 | ß | != | != | != | != |
+----+---+----+---+-----+-----+-----+-----+
关于排序规则的一些事情:
从 SQL Server 2000(是的,2000)开始不推荐使用
SQL_
排序规则。如果你可以避免使用它们,你应该(但这并不意味着如果没有迫切需要就去改变一堆东西!)。SQL_
排序规则的问题实际上只与VARCHAR
(即非 Unicode)数据有关,因为NVARCHAR
(即 Unicode)数据使用来自 OS。但不幸的是,VARCHAR
数据的排序和比较规则使用简单的映射,不包括更复杂的语言规则。这就是为什么ss
和ß
在使用相同的SQL_Latin1_General_CP1_CI_AS
排序规则存储为VARCHAR
时不等同的原因。当在单词中间使用时,这些已弃用的排序规则也无法降低破折号的权重。非SQL_
排序规则(即 Windows 排序规则)对VARCHAR
和NVARCHAR
使用相同的规则,因此VARCHAR
处理更健壮,更符合NVARCHAR
.从 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。这个特定的列是
NVARCHAR
(使用SQL_Latin1_General_CP1_CI_AS
的VARCHAR
列不等同于ss
和ß
)等等就这一列而言,SQL_Latin1_General_CP437_BIN2
和SQL_Latin1_General_CP850_BIN2
之间没有区别,因为 Unicode 是一个单一的、包罗万象的字符集。对于
VARCHAR
数据,会有不同,因为它们是不同的代码页(437 and 850), and both of those are different than the one that you are using now (CP1
== code page 1252)。虽然使用二进制排序规则通常是矫枉过正,但在这种情况下可能有必要,因为只有一种语言环境/文化不等同于
ß
与ss
: 匈牙利语。使用 Hungarian Collation 可能有一些您不想要(或至少不期望)的语言规则,因此二进制 Collation 似乎是这里更好的选择(只是不是您询问的 4 个中的任何一个 :-) .请记住,通过使用二进制排序规则,您不仅放弃了 所有 语言规则,而且还失去了将同一字符的不同版本等同起来的能力,例如A
(Latin Capital Letter A U+0041) andA
(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