SQL Server:更改现有数据库的排序规则是否安全?

Zac*_*ith 7 sql-server collation sql-server-2012

我是 SQL Server 的新手,不知道 T-SQL 可能不区分大小写的可能性。

我一直在通过拆分表和约束来规范化实体。我很害怕如果我现在更改排序规则,例如,如果“不区分大小写”的外键值指向具有不同大小写的 PK 值,那么其中一些约束可能无效。

如果我在数据库上运行这个查询(在基本数据库上测试):

ALTER DATABASE <db_name>
COLLATE SQL_Latin1_General_Cp1_CS_AS ;
GO
Run Code Online (Sandbox Code Playgroud)

如果违反某些约束会发生什么?

是否有任何其他原因我应该小心进行数据库范围的更改,例如更改排序规则?

Sol*_*zky 6

更改数据库的排序规则不会影响现有列。它会影响创建的未指定COLLATE子句(包括表变量)、字符串文字和变量值(不是变量名称解析,由实例级排序规则确定)的新非 XML 字符串列。这意味着,以下内容将受到影响:

IF (@Variable = 'string')
BEGIN
    ...
END;
Run Code Online (Sandbox Code Playgroud)

此更改还将影响数据库级别的元数据,例如模式名称、对象、列、索引等。意思是,以下两种情况将受到影响:

SELECT ...
FROM   sys.indexes si
WHERE  si.[name] = N'somename'; -- real name = SomeName
Run Code Online (Sandbox Code Playgroud)

和:

SELECT ...
FROM   dbo.sometable st -- real name = SomeTable
Run Code Online (Sandbox Code Playgroud)

在这两个示例中,它们将在不区分大小写的排序规则中工作,但在区分大小写的排序规则中分别不返回任何内容或错误。

最后,正如@JonathanFite 提醒我的那样,更改 DB Collat​​ion可能会影响涉及临时表的查询。临时表(不是表变量)中字符串列的默认排序规则是默认排序规则[tempdb](应该与 相同[model],这应该是实例默认值,除非有人恢复[model]来自具有不同默认排序规则的服务器),而不是本地数据库的排序规则。意思是,即使每次都会创建临时表,因此您可能希望它们像使用新排序规则的“新创建的表”一样,但实际上它们会像“现有表”一样运行,并且会继续像之前一样运行排序规则更改。如果您需要临时表中的字符串列使用新的排序规则,则需要COLLATE DATABASE_DEFAULT在使用 using 显式设置它们的排序规则CREATE TABLE 声明。

因此,您确实需要进行大量测试!

如果要更改现有列,则需要删除现有约束,发出ALTER TABLE ... ALTER COLUMN并重新创建约束。由于排序顺序可能不同,您还需要重建使用已更改排序规则的任何列的索引。

此外,最好不要使用以SQL_. 而是使用Latin1_General_100_CS_AS. SQL_自 SQL Server 2000发布以来,以 开头的排序规则已经过时(即使没有正式弃用)。他们对VARCHAR/ 8 位数据的处理已经过时,并且不符合更新的行为。不幸的是,出于向后兼容性的原因,美国英语安装的默认排序规则曾经是SQL_Latin1排序规则,如使用 SQL Server 排序规则MSDN 页面中所述:

为了向后兼容,默认的英语 (US) 排序规则是 SQL_Latin1_General*。

这也在设置MSDN 页面的排序规则设置中的默认排序规则图表中注明(点击 Control-F 并粘贴到 中sql_latin)。我相信这个默认值从 SQL Server 2014 开始更改为 Windows 排序规则,但是文档,即使是 SQL Server 2016 设置,仍然指向排序规则的 2008 R2 设置页面。


下面是一个脚本,用于查看更改数据库排序规则时的一些行为差异:

USE [master];
GO

IF (DB_ID(N'ChangeDatabaseCollationTest') IS NULL)
BEGIN
    CREATE DATABASE [ChangeDatabaseCollationTest] COLLATE Latin1_General_100_CI_AS;
END;
GO

USE [ChangeDatabaseCollationTest];
GO
-- Current DB Collation: Latin1_General_100_CI_AS

EXEC sp_help 'sys.objects';
-- Collation for [name] = Latin1_General_100_CI_AS

IF ('A' = 'a')
BEGIN
    SELECT 'Case INsensitive comparison works.';
END;
ELSE
BEGIN
    SELECT 'Case INsensitive comparison did NOT work.';
END;
-- Case INsensitive comparison works.


CREATE TABLE dbo.CaseTest_a (ID INT); -- success
SELECT * FROM dbo.CaseTest_A; -- success


CREATE TABLE dbo.CaseTest_A (ID INT); -- error:
-- Msg 2714, Level 16, State 6, Line 5
-- There is already an object named 'CaseTest_A' in the database.




ALTER DATABASE [ChangeCollationTest] COLLATE Latin1_General_100_CS_AS; -- success

IF ('A' = 'a')
BEGIN
    SELECT 'Case INsensitive comparison works.';
END;
ELSE
BEGIN
    SELECT 'Case INsensitive comparison did NOT work.';
END;
-- Case INsensitive comparison did NOT work.


SELECT * FROM dbo.CaseTest_A; -- error:
-- Msg 208, Level 16, State 1, Line 56
-- Invalid object name 'dbo.CaseTest_A'.


CREATE TABLE dbo.CaseTest_A (ID INT); -- success

EXEC sp_help 'sys.objects';
-- Collation for [name] = Latin1_General_100_CS_AS


ALTER DATABASE [ChangeCollationTest] COLLATE Latin1_General_100_CI_AS; -- error:
-- Msg 1505, Level 16, State 1, Line 23
-- The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for
--    the object name 'dbo.sysschobjs' and the index name 'nc1'. The duplicate key
--    value is (0, 1, CaseTest_A).
-- Msg 5072, Level 16, State 1, Line 23
-- ALTER DATABASE failed. The default collation of database 'ChangeCollationTest'
--    cannot be set to Latin1_General_100_CI_AS.
Run Code Online (Sandbox Code Playgroud)