在SQL Server中的字符类型都是有排序规则(collation)的,例如在下面的SQL语句中,我们建立了一个表变量@table_people,其中我们使用关键字COLLATE给列[Name]定义了排序规则"Chinese_PRC_CI_AS",也给列[Description]定义了排序规则"SQL_Latin1_General_CP1_CI_AS":
DECLARE @table_people TABLE ( [Name] NVARCHAR(50) COLLATE Chinese_PRC_CI_AS, [Description] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS, [Age] INT ) INSERT INTO @table_people([Name],[Description],[Age]) VALUES (N'张三' COLLATE Chinese_PRC_CI_AS,N'这个人叫张三' COLLATE SQL_Latin1_General_CP1_CI_AS,31), (N'李四' COLLATE Chinese_PRC_CI_AS,N'这个人叫李四' COLLATE SQL_Latin1_General_CP1_CI_AS,32), (N'王五' COLLATE Chinese_PRC_CI_AS,N'这个人叫王五' COLLATE SQL_Latin1_General_CP1_CI_AS,33) SELECT * FROM @table_people
这段SQL代码可以成功执行,因为我们使用了COLLATE关键字为每一个字符串指定了相应的排序规则。
但是,如果我们对不同排序规则的字符类型进行操作的时候就会报错,如下所示:
DECLARE @table_people TABLE ( [Name] NVARCHAR(50) COLLATE Chinese_PRC_CI_AS, [Description] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS, [Age] INT ) INSERT INTO @table_people([Name],[Description],[Age]) VALUES (N'张三' COLLATE Chinese_PRC_CI_AS,N'这个人叫张三' COLLATE SQL_Latin1_General_CP1_CI_AS,31), (N'李四' COLLATE Chinese_PRC_CI_AS,N'这个人叫李四' COLLATE SQL_Latin1_General_CP1_CI_AS,32), (N'王五' COLLATE Chinese_PRC_CI_AS,N'王五' COLLATE SQL_Latin1_General_CP1_CI_AS,33) SELECT * FROM @table_people WHERE [Name]=[Description]--Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Chinese_PRC_CI_AS" in the equal to operation.
上面的SQL语句,会在最后的SELECT语句报错:
Msg 468, Level 16, State 9, Line 14
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Chinese_PRC_CI_AS" in the equal to operation.
由于我们在SELECT语句的WHERE条件中比较了列[Name]和列[Description]的值,但是[Name]和[Description]是两个排序规则不同的列,不能直接进行比较操作,所以我们要使用COLLATE关键字对排序规则进行转换后,才能进行比较:
DECLARE @table_people TABLE ( [Name] NVARCHAR(50) COLLATE Chinese_PRC_CI_AS, [Description] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS, [Age] INT ) INSERT INTO @table_people([Name],[Description],[Age]) VALUES (N'张三' COLLATE Chinese_PRC_CI_AS,N'这个人叫张三' COLLATE SQL_Latin1_General_CP1_CI_AS,31), (N'李四' COLLATE Chinese_PRC_CI_AS,N'这个人叫李四' COLLATE SQL_Latin1_General_CP1_CI_AS,32), (N'王五' COLLATE Chinese_PRC_CI_AS,N'王五' COLLATE SQL_Latin1_General_CP1_CI_AS,33) --将列[Description]的排序规则转换为Chinese_PRC_CI_AS SELECT * FROM @table_people WHERE [Name]=[Description] COLLATE Chinese_PRC_CI_AS --将列[Name]的排序规则转换为SQL_Latin1_General_CP1_CI_AS SELECT * FROM @table_people WHERE [Name] COLLATE SQL_Latin1_General_CP1_CI_AS=[Description]
由于现在我们使用COLLATE关键字对列[Name]和列[Description]的排序规则进行了转换,保证了SELECT语句WHERE条件中等号两边字符类型的排序规则是相同的,所以现在两个SELECT语句都不会报错,并成功返回了查询结果。
前面我们说了,COLLATE关键字可以指定SQL Server中字符类型的排序规则,它可以作用于char、varchar、text、nchar、nvarchar、ntext数据类型。
当我们在操作临时表中的字符类型时,就很有可能需要使用COLLATE关键字对排序规则进行转换,因为SQL Server中的临时表存在于系统数据库tempdb中,而系统数据库tempdb的排序规则很有可能和用户创建数据库的排序规则不同,所以如果我们在用户创建的数据库中使用了临时表,并对字符类型进行了操作,那么很有可能会因为系统数据库tempdb的排序规则和用户创建数据库不一致而报错,这时候我们就需要使用COLLATE关键字对不同排序规则的字符类型进行转换,这是一个比较常用的使用场景。另外在用户创建的数据库中使用表变量一般不会存在排序规则的问题,原因如下:
The collations for the string type (char, nchar, varchar, nvarchar) columns, if not given explicitly, will take
the database collation of TempDB in a temp table, and inherit the collation of the current user database in a table variable. If not handled correctly, string comparing will fail.
参考: Local Temporary Tables and Table Variables
我们还可以在COLLATE关键字后使用database_default,指定字符类型的排序规则为当前数据库使用的排序规则:
DECLARE @table_people TABLE ( [Name] NVARCHAR(50) COLLATE database_default, [Description] NVARCHAR(50) COLLATE database_default, [Age] INT ) INSERT INTO @table_people([Name],[Description],[Age]) VALUES (N'张三' COLLATE database_default,N'这个人叫张三' COLLATE database_default,31), (N'李四' COLLATE database_default,N'这个人叫李四' COLLATE database_default,32), (N'王五' COLLATE database_default,N'王五' COLLATE database_default,33) SELECT * FROM @table_people WHERE [Name]=[Description]
可以参考下面的微软文档,对COLLATE关键字进行详细的了解:
COLLATE (Transact-SQL)