首先,对这么长的答案表示歉意,因为我觉得当人们谈论排序规则,排序顺序,代码页等术语时,仍然存在很多困惑。
从BOL:
SQL Server中的归类为数据提供排序规则,大小写和重音敏感性属性。与字符数据类型(例如char和varchar)一起使用的排序规则规定了代码页以及可以为该数据类型表示的相应字符。无论您是要安装SQL Server的新实例,还原数据库备份还是将服务器连接到客户端数据库,理解您要使用的数据的语言环境要求,排序顺序以及大小写和重音敏感性都非常重要。 。
这意味着归类非常重要,因为它指定了有关如何对数据字符串进行排序和比较的规则。
注意:有关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
结果将是:
从上面的结果来看,唯一的区别是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
测试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.
从以上结果可以看出,我们无法直接比较具有不同归类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')
-在两个表上创建索引
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)
---运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments = 'kin_test1'
GO
---这将具有隐式转换
---运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = 'kin_test1'
GO
---这将不会进行隐式转换
之所以隐式转换是因为,我有我的数据库和服务器的排序规则既作为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
-运行查询
DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = N'kin_test1'
GO
请注意,第一个查询能够执行索引查找,但必须执行隐式转换,而第二个查询执行索引扫描,这在扫描大型表时在性能方面被证明是效率低下的。
结论:
- 所有上述测试表明,正确的排序规则对于数据库服务器实例非常重要。
SQL_Latin1_General_CP1_CI_AS
是带有规则的SQL排序规则,该规则使您可以对Unicode和非Unicode数据进行排序。
- 如上面的测试所示,在将unichar和非unicode数据进行比较时,SQL归类将无法使用Index,在将nvarchar数据与varchar数据进行比较时,它会进行索引扫描而不是查找。
Latin1_General_CI_AS
是Windows排序规则的排序规则,允许您对Unicode和非Unicode相同的数据进行排序。
- 在比较unicode和非unicode数据时,Windows排序规则仍可以使用Index(在上面的示例中为Index seek),但是您会发现性能略有下降。
- 强烈建议阅读Erland Sommarskog的答案以及他所指向的连接项。
这将使我在#temp表上没有问题,但是有陷阱吗?
请参阅上面的答案。
如果不使用SQL 2008的“当前”排序规则,是否会丢失任何类型的功能或特性?
这完全取决于您所指的功能。整理是存储和排序数据。
当我们从2008年迁移到SQL 2012时(例如,在2年内)怎么办?那我有问题吗?我会在某个时候被迫去Latin1_General_CI_AS吗?
不能担保!由于情况可能会发生变化,并且与Microsoft的建议保持一致总是很有益的,因此您需要了解您的数据和我上面提到的陷阱。也可参考这个和这个连接项目。
我读到一些DBA的脚本完成了完整数据库的行,然后使用新的排序规则将插入脚本运行到数据库中-我对此感到非常害怕和警惕-您会建议这样做吗?
当您要更改排序规则时,此类脚本很有用。我发现自己多次更改数据库排序规则以匹配服务器排序规则,并且我有一些脚本可以使它整洁。让我知道你是否需要它。
参考文献: