SQL Server SQL_Latin1_General_CP1_CI_AS 可以安全地转换为 Latin1_General_CI_AS 吗?

Posted

技术标签:

【中文标题】SQL Server SQL_Latin1_General_CP1_CI_AS 可以安全地转换为 Latin1_General_CI_AS 吗?【英文标题】:Can SQL Server SQL_Latin1_General_CP1_CI_AS be safely converted to Latin1_General_CI_AS? 【发布时间】:2011-09-11 22:06:21 【问题描述】:

我们有一个旧数据库,其中一些(较旧的)列使用“SQL_Latin1_General_CP1_CI_AS”,最近的更改使用了“Latin1_General_CI_AS”。

这很痛苦,因为连接需要额外的 COLLATE 语句才能工作。

我想将所有内容都带到“Latin1_General_CI_AS”。据我所知,它们或多或少是相同的排序规则,在此过程中我不会丢失数据...

有人知道是不是这样吗?

【问题讨论】:

【参考方案1】:

这里有一个更完整的答案:

https://www.olcot.co.uk/revised-difference-between-collation-sql_latin1_general_cp1_ci_as-and-latin1_general_ci_as/

这些排序规则之间的主要区别在于它们如何应用字符扩展规则。某些拉丁字符可能会扩展为多个字符。 SQL_xxxx 排序规则在处理非 unicode 文本时可能会忽略这些字符扩展,但会将它们应用于 unicode 文本。因此:在使用一种排序规则与另一种排序规则时,连接、排序和比较可能会返回不同的结果。

例子:

Latin1_General_CI_AS 下,这两个语句返回同一组记录,因为ß 扩展为ss

SELECT * FROM MyTable3 WHERE Comments = 'strasse'
SELECT * FROM MyTable3 WHERE Comments = 'straße'

当使用SQL_Latin1_General_CP1_CI_AS 时,上述语句返回不同的记录,因为ß 被视为与ss 不同的字符。

【讨论】:

+1 用于查找和提及字符扩展。我在我的answer 中包含了它(以及提到它的 Unicode 文档的链接),并添加了更多需要注意的项目:-)。 如果CommentsNVARCHAR 怎么办? SQL_Latin1_General_CP1_CI_AS 不支持 Unicode,因此回退到处理 'ass' = 'aß' 的 Latin1_General_CI_ASNVARCHAR 怎么能强制比较 'ss' 'ß'? @Hannobo 在表、临时表或表变量中定义任何文本字段时,您可以指定排序规则。对于许多基于文本的操作,您还可以通过添加 `COLLATE desired_collat​​ion` 来指定排序规则。我不确定,但您可以在定义变量时指定排序规则。由于我是在头脑中回答这个问题,因此我建议您查看 SQL Server T-SQL 文档以了解语法和更多详细信息。 @Zarepheth 我做了,但文档和测试表明,任何排序规则(在字段上或在查询中)都回退到 NVARCHAR 的标准 Unicode。我发现的唯一方法是动态转换为 VARCHAR。 @Hannobo(和 Zarepheth):不要转换为 VARCHAR,因为这会导致数据丢失。您需要使用匈牙利语或二进制排序规则,因为它们是唯一不等同于这些特定字符的排序规则。所以试试COLLATE Hungarian_Technical_100_CI_AS_SCCOLLATE Latin1_General_100_BIN2。更多详情请看我的回答here和here。此外,变量(和字符串文字)始终具有当前数据库默认排序规则的排序规则,并且不能被赋予不同的排序规则。【参考方案2】:

如果您要更改数据库的排序规则,那么您肯定应该知道一些事情,以便您可以做出相应的计划:

关于数据丢失的可能性:

NVARCHAR 字段都是 Unicode,这是一个单一的字符集,因此这些字段不会有任何数据丢失(这也包括同样存储为 UTF-16 Little Endian 的 XML 字段)。存储对象/列/索引/等名称的元数据字段都是NVARCHAR,所以不用担心这些。 VARCHAR 具有不同排序规则但不同排序规则之间的代码页相同的字段不会成为问题,因为代码页是字符集。 VARCHAR 具有不同排序规则并移动到不同代码页的字段(更改排序规则时)可能如果正在使用的任何字符未在新代码页中表示,则会丢失数据。但是,这只是在物理更改特定字段的排序规则(如下所述)时出现的问题,并且不会在更改数据库的默认排序规则时发生。

局部变量和字符串文字从数据库默认值中获取它们的排序规则。更改数据库默认值将更改用于局部变量和字符串文字的排序规则。但是更改数据库的默认排序规则不会更改用于该数据库表中现有字符串列的排序规则。在将列与文字和/或变量进行比较或连接时,这通常不会导致任何问题,因为由于排序优先级,文字和变量将采用列的排序规则。唯一的潜在问题是代码页转换可能发生在列排序规则使用的代码页中不可用的值介于 128 到 255 之间的字符。

如果您期望谓词/比较/排序/连接/等在更改数据库的默认排序规则时表现不同,那么您需要使用以下命令显式更改该列的排序规则:

ALTER TABLE [table_name]
   ALTER COLUMN [column_name]
   same_datatype
   same_NULL_or_NOT NULL_setting
   COLLATE name_of_Database_default_Collation;

请务必指定当前正在使用的完全相同数据类型和NULL / NOT NULL 设置,否则如果它们不是默认值,它们可以恢复为默认值。之后,如果任何字符串列上的任何索引刚刚更改了其排序规则,那么您需要重建这些索引。

更改数据库的默认排序规则将更改某些特定于数据库的元数据的排序规则,例如sys.objectssys.columnssys.indexes 等中的name 字段。过滤这些系统针对局部变量或字符串文字的视图不会成为问题,因为 Collat​​ion 将在双方发生变化。但是,如果您将任何本地系统视图加入到字符串字段上的临时表中,并且本地数据库和tempdb 之间的数据库级排序规则不匹配,那么您将收到“排序规则不匹配”错误。这将在下面与补救措施一起讨论。

这两个排序规则之间的一个区别在于它们如何对VARCHAR 数据的某些字符进行排序(这不会影响NVARCHAR 数据)。非 EBCDIC SQL_ 排序规则对 VARCHAR 数据使用所谓的“字符串排序”,而所有其他排序规则,甚至是非 EBCDIC SQL_ 排序规则的 NVARCHAR 数据,使用所谓的“字排序” ”。不同之处在于,在“单词排序”中,破折号- 和撇号'(可能还有一些其他字符?)的权重非常低,除非字符串没有其他差异,否则基本上会被忽略。要查看此行为的实际效果,请运行以下命令:

DECLARE @Test TABLE (Col1 VARCHAR(10) NOT NULL);
INSERT INTO @Test VALUES ('aa');
INSERT INTO @Test VALUES ('ac');
INSERT INTO @Test VALUES ('ah');
INSERT INTO @Test VALUES ('am');
INSERT INTO @Test VALUES ('aka');
INSERT INTO @Test VALUES ('akc');
INSERT INTO @Test VALUES ('ar');
INSERT INTO @Test VALUES ('a-f');
INSERT INTO @Test VALUES ('a_e');
INSERT INTO @Test VALUES ('a''kb');

SELECT * FROM @Test ORDER BY [Col1] COLLATE SQL_Latin1_General_CP1_CI_AS;
-- "String Sort" puts all punctuation ahead of letters

SELECT * FROM @Test ORDER BY [Col1] COLLATE Latin1_General_100_CI_AS;
-- "Word Sort" mostly ignores dash and apostrophe

返回:

String Sort
-----------
a'kb
a-f
a_e
aa
ac
ah
aka
akc
am
ar

和:

Word Sort
---------
a_e
aa
ac
a-f
ah
aka
a'kb
akc
am
ar

虽然您将“丢失”“字符串排序”行为,但我不确定我是否会将其称为“功能”。这是一种被认为不受欢迎的行为(事实证明,它没有被带到任何 Windows 排序规则中)。但是,这 是两种排序规则之间行为的明显差异(同样,仅适用于非 EBCDIC VARCHAR 数据),并且您可能有基于“字符串排序”的代码和/或客户期望“ 行为。 这需要测试您的代码,并且可能需要研究这种行为变化是否会对用户产生负面影响。

SQL_Latin1_General_CP1_CI_ASLatin1_General_100_CI_AS 之间的另一个区别是能够对VARCHAR 数据执行Expansions(NVARCHAR 数据已经可以对大多数SQL_ 排序规则执行这些操作),例如将æ 处理为如果是ae:

IF ('æ' COLLATE SQL_Latin1_General_CP1_CI_AS =
    'ae' COLLATE SQL_Latin1_General_CP1_CI_AS)
BEGIN
  PRINT 'SQL_Latin1_General_CP1_CI_AS';
END;

IF ('æ' COLLATE Latin1_General_100_CI_AS =
    'ae' COLLATE Latin1_General_100_CI_AS)
BEGIN
  PRINT 'Latin1_General_100_CI_AS';
END;

返回:

Latin1_General_100_CI_AS

您在这里“失去”的唯一一件事是能够进行这些扩展。一般来说,这是迁移到 Windows 排序规则的另一个好处。但是,就像从“字符串排序”到“单词排序”的移动一样,同样需要注意:这两个排序规则之间的行为存在明显差异(同样,仅针对 VARCHAR 数据),您可能有代码和/或基于没有这些映射的客户期望。 这需要测试您的代码,并且可能需要研究这种行为变化是否会对用户产生负面影响。

(首先在@Zarepheth 的answer 中注明,并在此处进行了扩展)

另一个区别(这也是迁移到 Windows 排序规则的一个好处)是过滤在 NVARCHAR 文字/变量/列上索引的 VARCHAR 列,您将不再使VARCHAR 专栏。这是由于 Windows 排序规则对 VARCHARNVARCHAR 数据使用相同的 Unicode 排序和比较规则。因为这两种类型之间的排序顺序相同,所以当VARCHAR 数据转换为NVARCHAR 时(显式或隐式由于数据类型优先级),索引中的项目顺序仍然有效。有关此行为的更多详细信息,请参阅我的帖子:Impact on Indexes When Mixing VARCHAR and NVARCHAR Types。

服务器级Collat​​ion用于设置系统数据库的Collat​​ion,包括[model][model] 数据库用作创建新数据库的模板,其中包括每次服务器启动时的[tempdb]。因此,如果数据库的默认排序规则与实例的默认排序规则不匹配并且您将本地表连接到字符串字段上的临时表,那么您将收到 Collat​​ion-mismatch 错误。幸运的是,有一种简单的方法可以更正执行CREATE #TempTable 时“当前”的数据库与[tempdb] 之间的排序规则差异。创建临时表时,使用 COLLATE 子句声明排序规则(在字符串列上)并使用特定排序规则(如果您知道数据库将始终使用该排序规则)或 DATABASE_DEFAULT(如果您不始终知道将执行此代码的数据库的排序规则):

CREATE TABLE #Temp (Col1 NVARCHAR(40) COLLATE DATABASE_DEFAULT);

这对于表变量不是必需的,因为它们从“当前”数据库中获取默认排序规则。但是,如果您同时拥有表变量和临时表并将它们连接到字符串字段,那么您将需要使用COLLATE specific_collationCOLLATE DATABASE_DEFAULT,如上所示。

服务器级排序规则还控制局部变量名称、CURSOR 变量名称和 GOTO 标签。虽然这些都不会受到本问题中处理的具体变化的影响,但至少需要注意。

如果有多个版本可用,最好使用所需排序规则的最新版本。从 SQL Server 2005 开始,引入了“90”系列排序规则,SQL Server 2008 引入了“100”系列排序规则。您可以使用以下查询找到这些排序规则:

SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]90[_]%'; -- 476

SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]100[_]%'; -- 2686

另外,虽然问题询问不区分大小写的排序规则,但应注意,如果其他人希望进行类似的更改但使用区分大小写的排序规则,那么 SQL Server 排序规则和 Windows 之间的另一个区别排序规则,仅用于 VARCHAR 数据,是先排序。意思是,如果您同时拥有Aa,则SQL_ 排序规则将A 排序在a 之前,而非SQL_ 排序规则(以及SQL_ 排序规则在处理NVARCHAR 时) data) 将在A 之前排序a

有关更改数据库或整个实例的排序规则的更多信息和详细信息,请参阅我的帖子:Changing the Collation of the Instance, the Databases, and All Columns in All User Databases: What Could Possibly Go Wrong?

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

【讨论】:

@Jay 谢谢!请重新阅读,因为我刚刚做了一些调整。一些关于:文字/变量和索引的信息并不完全正确。现在它是。我添加了两个链接,指向我提交此答案时不存在的帖子。【参考方案3】:

在这个 MSDN 论坛上有更多信息:

http://social.msdn.microsoft.com/Forums/en-US/sqlgetstarted/thread/196b4586-1338-434d-ba8c-49fa3c9bdeeb/

其中规定:

如果排序规则是 SQL_Latin1_General_CP1_CI_AS 或 Latin1_General_CI_AS,您应该会发现差别不大,但两者都有比另一个更快或更慢的实例。

Latin1_General_CI_AS :- Latin1-General,不区分大小写,重音- 敏感,不区分假名,不区分宽度

SQL_Latin1_General_CP1_CI_AS:- Latin1-General,不区分大小写, Unicode 区分重音、不区分假名、不区分宽度 数据,非 Unicode 数据的代码页 1252 上的 SQL Server 排序顺序 52

因此在我看来你不应该看到差异,特别是如果你的数据只是 a-z0-9

【讨论】:

“您应该会发现差别不大”,除非您想将 INFORMATION_SCHEMA 的结果与临时表连接或比较,而 AzureSQL 表示不能。【参考方案4】:
SELECT * FROM ::fn_helpcollations()
WHERE name IN (
'SQL_Latin1_General_CP1_CI_AS',
'Latin1_General_CI_AS'
)

...给...

Latin1_General_CI_AS: Latin1-General、不区分大小写、区分重音、不区分假名、不区分宽度

SQL_Latin1_General_CP1_CI_AS: Latin1-General、不区分大小写、区分重音、不区分假名、不区分宽度适用于 Unicode 数据,SQL Server 排序顺序为 52,适用于非 Unicode 数据的代码页 1252

因此,我推断所使用的代码页是相同的(Latin1-General => 1252),所以您应该不会遇到数据丢失 - 如果有任何更改后-转换可能是排序顺序 - 这可能无关紧要。

【讨论】:

【参考方案5】:

为此,请转到数据库的属性并选择选项。

然后将集合类型更改为 SQL_Latin1_General_CP1_CS_AS。

【讨论】:

以上是关于SQL Server SQL_Latin1_General_CP1_CI_AS 可以安全地转换为 Latin1_General_CI_AS 吗?的主要内容,如果未能解决你的问题,请参考以下文章

[SQL Server]无法解决 equal to 运算中 "SQL_Latin1_General_CP1_CI_AS" 和 "Chinese_PRC_CI_AS&quo

Android - 没有这样的排序规则:SQL_Latin1_General_CP1_CI_AS

无法解决等于操作中“Cyrillic_General_CI_AS”和“SQL_Latin1_General_CP1_CI_AS”之间的排序规则冲突

无法解决等于操作中“SQL_Latin1_General_CP1_CI_AS”和“Latin1_General_CI_AS”之间的排序规则冲突

无法解决等于操作中“SQL_Latin1_General_CP1_CI_AS”和“Latin1_General_CI_AS”之间的排序规则冲突[重复]

无法解决等于操作中“SQL_Latin1_General_Pref_CP1_CI_AS”和“Latin1_General_CI_AS”之间的排序规则冲突