Pet*_*ock 18 sql-server-2005 sql-server-2008 collation
我们正在从 SQL 2005 [Instance and DB has collation of SQL_Latin1_General_CP1_CI_AS
] 迁移到 SQL 2008 [默认为Latin1_General_CI_AS
]。
我完成了 SQL 2008 R2 安装,并使用了默认Latin1_General_CI_AS
排序规则,数据库的恢复仍然在SQL_Latin1_General_CP1_CI_AS
. 发生了例外的问题 -Latin1_General_CI_AS
数据库 所在的 #temp 表, SQL_Latin1_General_CP1_CI_AS
这就是我现在所在的位置 - 我现在需要关于陷阱的建议。
在安装 SQL 2008 R2 时,我可以选择在安装时使用'SQL Collation, used for backwards compatibility'
,我可以选择与 2005 数据库相同的排序规则:SQL_Latin1_General_CP1_CI_AS
。
这将使我不会遇到#temp 表的问题,但是有陷阱吗?
如果不使用 SQL 2008 的“当前”归类,我会失去任何功能或特性吗?
我会在某个时候被迫去Latin1_General_CI_AS
吗?
我读到一些 DBA 的脚本完成了完整数据库的行,然后使用新的排序规则将插入脚本运行到数据库中 - 我对此非常害怕和警惕 - 你会建议这样做吗?
Kin*_*hah 20
首先,对这么长的答案表示歉意,因为我觉得当人们谈论诸如整理、排序顺序、代码页等术语时仍然存在很多困惑。
来自BOL:
SQL Server 中的排序规则为您的数据提供排序规则、大小写和区分重音的属性。与字符数据类型(例如 char 和 varchar)一起使用的排序规则规定了可以为该数据类型表示的代码页和相应的字符。无论您是安装 SQL Server 的新实例、还原数据库备份还是将服务器连接到客户端数据库,了解您将使用的数据的区域设置要求、排序顺序以及区分大小写和重音都很重要.
这意味着 Collation 非常重要,因为它指定了如何对数据的字符串进行排序和比较的规则。
注意:关于COLLATIONPROPERTY 的更多信息
现在让我们先了解一下区别......
在 T-SQL 下运行:
SELECT *
FROM::fn_helpcollations()
WHERE NAME IN (
'SQL_Latin1_General_CP1_CI_AS'
,'Latin1_General_CI_AS'
)
GO
SELECT 'SQL_Latin1_General_CP1_CI_AS' AS 'Collation'
,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'CodePage') AS 'CodePage'
,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'LCID') AS 'LCID'
,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'Version') AS 'Version'
UNION ALL
SELECT 'Latin1_General_CI_AS' AS 'Collation'
,COLLATIONPROPERTY('Latin1_General_CI_AS', 'CodePage') AS 'CodePage'
,COLLATIONPROPERTY('Latin1_General_CI_AS', 'LCID') AS 'LCID'
,COLLATIONPROPERTY('Latin1_General_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
,COLLATIONPROPERTY('Latin1_General_CI_AS', 'Version') AS 'Version'
GO
Run Code Online (Sandbox Code Playgroud)
结果将是:
查看上面的结果,唯一的区别是 2 个排序规则之间的排序顺序。但事实并非如此,您可以看到原因如下:
测试 1:
--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
DROP TABLE Table_Latin1_General_CI_AS;
IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;
-- Create a table using collation Latin1_General_CI_AS
CREATE TABLE Table_Latin1_General_CI_AS (
ID INT IDENTITY(1, 1)
,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
)
-- add some data to it
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('Kin_Tester1')
-- Create second table using collation SQL_Latin1_General_CP1_CI_AS
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
ID INT IDENTITY(1, 1)
,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
)
-- add some data to it
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('Kin_Tester1')
--Now try to join both tables
SELECT *
FROM Table_Latin1_General_CI_AS LG
INNER JOIN Table_SQL_Latin1_General_CP1_CI_AS SLG ON LG.Comments = SLG.Comments
GO
Run Code Online (Sandbox Code Playgroud)
测试 1 的结果:
Msg 468, Level 16, State 9, Line 35
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.
Run Code Online (Sandbox Code Playgroud)
从上面的结果我们可以看出,我们不能直接比较具有不同排序规则COLLATE
的列上的值,您必须使用来比较列值。
测试 2:
正如 Erland Sommarskog在 msdn 上的讨论中指出的那样,主要区别在于性能。
--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
DROP TABLE Table_Latin1_General_CI_AS;
IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;
-- Create a table using collation Latin1_General_CI_AS
CREATE TABLE Table_Latin1_General_CI_AS (
ID INT IDENTITY(1, 1)
,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
)
-- add some data to it
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_tester1')
-- Create second table using collation SQL_Latin1_General_CP1_CI_AS
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
ID INT IDENTITY(1, 1)
,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
)
-- add some data to it
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_tester1')
Run Code Online (Sandbox Code Playgroud)
--- 在两个表上创建索引
CREATE INDEX IX_LG_Comments ON Table_Latin1_General_CI_AS(Comments)
go
CREATE INDEX IX_SLG_Comments ON Table_SQL_Latin1_General_CP1_CI_AS(Comments)
Run Code Online (Sandbox Code Playgroud)
--- 运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments = 'kin_test1'
GO
Run Code Online (Sandbox Code Playgroud)
--- 这将有 IMPLICIT 转换
--- 运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = 'kin_test1'
GO
Run Code Online (Sandbox Code Playgroud)
--- 这不会有隐式转换
之所以隐式转换是因为,我有我的数据库和服务器的排序规则既作为SQL_Latin1_General_CP1_CI_AS
和表Table_Latin1_General_CI_AS有列注释定义为VARCHAR(50)
与COLLATE Latin1_General_CI_AS,所以在查找SQL Server必须做一个隐式转换。
测试 3:
使用相同的设置,现在我们将 varchar 列与 nvarchar 值进行比较,以查看执行计划的变化。
-- 运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments = (SELECT N'kin_test1' COLLATE Latin1_General_CI_AS)
GO
Run Code Online (Sandbox Code Playgroud)
-- 运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = N'kin_test1'
GO
Run Code Online (Sandbox Code Playgroud)
请注意,第一个查询能够执行索引查找,但必须执行隐式转换,而第二个查询执行索引扫描,这在扫描大型表时证明在性能方面效率低下。
结论 :
SQL_Latin1_General_CP1_CI_AS
是 SQL 排序规则,允许您对 unicode 和非 unicode 的数据进行排序的规则是不同的。Latin1_General_CI_AS
是 Windows 排序规则,允许您对 unicode 和非 unicode 的数据进行排序的规则是相同的。这将使我不会遇到#temp 表的问题,但是有陷阱吗?
看我上面的回答。
如果不使用 SQL 2008 的“当前”归类,我会失去任何功能或特性吗?
这一切都取决于您所指的功能/特性。整理是对数据的存储和排序。
当我们从 2008 年迁移到 SQL 2012 时(例如 2 年后)呢?那我会有问题吗?我会在某个时候被迫去 Latin1_General_CI_AS 吗?
不能保证!由于情况可能会发生变化,并且遵循 Microsoft 的建议总是好的 + 您需要了解您的数据和我上面提到的陷阱。另请参阅此和此连接项。
我读到一些 DBA 的脚本完成了完整数据库的行,然后使用新的排序规则将插入脚本运行到数据库中 - 我对此非常害怕和警惕 - 你会建议这样做吗?
当您想更改排序规则时,此类脚本很有用。我发现自己多次更改数据库的排序规则以匹配服务器排序规则,并且我有一些脚本可以做到这一点。如果您需要,请告诉我。
参考 :
除了@Kin 在他的回答中详细说明的内容之外,在切换服务器(即实例的)默认排序规则时还有一些需要注意的事项(水平线上方的项目与问题中提到的两个排序规则直接相关;项目低于水平线的都是相关的一般):
如果您的数据库的默认排序规则没有改变,那么@Kin 的回答中描述的“隐式转换”性能问题应该不是问题,因为字符串文字和局部变量使用数据库的默认排序规则,而不是服务器的。实例级别排序规则更改但数据库级别排序规则未更改的场景的唯一影响是(两者都在下面详细描述):
这两个排序规则之间的一个区别在于它们如何对VARCHAR
数据的某些字符进行排序(这不会影响NVARCHAR
数据)。非 EBCDICSQL_
归类对VARCHAR
数据使用所谓的“字符串排序” ,而所有其他归类,甚至NVARCHAR
是非 EBCDICSQL_
归类的数据,都使用所谓的“字排序”。不同之处在于,在“Word Sort”中,破折号-
和撇号'
(可能还有其他一些字符?)的权重非常低,除非字符串中没有其他差异,否则基本上会被忽略。要查看此行为的实际效果,请运行以下命令:
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
Run Code Online (Sandbox Code Playgroud)
返回:
String Sort
-----------
a'kb
a-f
a_e
aa
ac
ah
aka
akc
am
ar
Run Code Online (Sandbox Code Playgroud)
和:
Word Sort
---------
a_e
aa
ac
a-f
ah
aka
a'kb
akc
am
ar
Run Code Online (Sandbox Code Playgroud)
虽然您将“失去”“字符串排序”行为,但我不确定我是否会称其为“功能”。这是一种被认为是不受欢迎的行为(事实证明它没有被带到任何 Windows 排序规则中)。然而,这是两个归类(再次,只是为了非EBCDIC之间的行为的一定的差异VARCHAR
数据),并且可能必须基于“字符串排序”行为的代码和/或客户的期望。这需要测试您的代码并可能进行研究以查看这种行为变化是否会对用户产生任何负面影响。
之间的另一个区别SQL_Latin1_General_CP1_CI_AS
和Latin1_General_100_CI_AS
是做的能力展开的VARCHAR
数据(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;
Run Code Online (Sandbox Code Playgroud)
返回:
Latin1_General_100_CI_AS
Run Code Online (Sandbox Code Playgroud)
您在这里“失去”的唯一一件事就是无法进行这些扩展。一般来说,这是迁移到 Windows 排序规则的另一个好处。然而,就像从“字符串排序”到“词排序”的移动一样,同样的谨慎适用:这两个排序规则之间的行为有明显差异(同样,仅针对VARCHAR
数据),并且您可能有代码和/或客户基于没有这些映射的期望。这需要测试您的代码并可能进行研究以查看这种行为变化是否会对用户产生任何负面影响。
(在@Zarepheth 的这个 SO 回答中首先指出:SQL Server SQL_Latin1_General_CP1_CI_AS 可以安全地转换为 Latin1_General_CI_AS 吗?)
服务器级排序规则用于设置系统数据库的排序规则,其中包括[model]
. 该[model]
数据库用作创建新数据库的模板,包括[tempdb]
在每次服务器启动时。但是,即使服务器级别的排序规则更改了 的排序规则[tempdb]
,也有一种简单的方法可以更正CREATE #TempTable
执行时“当前”的数据库与 之间的排序规则差异[tempdb]
。创建临时表时,使用COLLATE
子句声明排序规则并指定排序规则DATABASE_DEFAULT
:
CREATE TABLE #Temp (Col1 NVARCHAR(40) COLLATE DATABASE_DEFAULT);
Run Code Online (Sandbox Code Playgroud)如果有多个版本可用,最好使用所需归类的最新版本。从 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
Run Code Online (Sandbox Code Playgroud)
由于您使用的SQL Server 2008 R2中,你应该使用Latin1_General_100_CI_AS
代替Latin1_General_CI_AS
。
在进行区分大小写的排序时,这些特定排序规则(即SQL_Latin1_General_CP1_CS_AS
和Latin1_General_100_CS_AS
)的区分大小写版本之间的区别在于大写和小写字母的顺序。这也会影响[start-end]
可与LIKE
运算符和PATINDEX
函数一起使用的单字符类范围(即)。以下三个查询显示了这种对排序和字符范围的影响。:
SELECT tmp.col AS [Upper-case first]
FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
WHERE tmp.col LIKE '%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Upper-case first
SELECT tmp.col AS [Lower-case first]
FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
WHERE tmp.col LIKE '%[A-C]%' COLLATE Latin1_General_100_CS_AS
ORDER BY tmp.col COLLATE Latin1_General_100_CS_AS; -- Lower-case first
SELECT tmp.col AS [Lower-case first]
FROM (VALUES (N'a'), (N'A'), (N'b'), (N'B'), (N'c'), (N'C')) tmp(col)
WHERE tmp.col LIKE N'%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Lower-case first
Run Code Online (Sandbox Code Playgroud)
让大写在小写之前排序(对于同一个字母)的唯一方法是使用支持该行为的 31 个排序规则之一,即Hungarian_Technical_*
排序规则和少数SQL_
排序规则(仅支持VARCHAR
数据的这种行为))。
对于此特定更改不太重要,但仍然值得了解,因为如果将服务器更改为二进制或区分大小写的排序规则,服务器级别排序规则也会影响:
sysname
数据类型的名称解析
意思是,如果您或“最近离开的程序员”显然对所有错误代码负责;-) 不小心大小写并声明了一个变量 as@SomethingID
但随后将其引用@somethingId
,如果转移到案例中,那将会中断- 敏感或二进制排序规则。类似地,如果使用区分大小写或二进制排序规则移动到实例中,使用该sysname
数据类型但将其引用为SYSNAME
、SysName
或其他非小写形式的代码也会中断。
归档时间: |
|
查看次数: |
19927 次 |
最近记录: |