SQL Server的 排序规则(collation)冲突和解决方案[转]

关于SQL Server的排序规则,估计大家都不陌生,在创建数据库时我们经常要选择一种排序规则(conllation),一般我们会留意到每一种语言的排序规则都有许多种,比如标准大陆简体中文Chinese_PRC的排序规则就有数十种之多

【点击查看原图】

 

这些排序规则有什么作用呢?让我们先来看看MS官方的解释:

排序规则指定了表示每个字符的位模式。它还指定了用于排序和比较字符的规则。排序规则具有下面的特征:

  • 语言
  • 区分大小写
  • 区分重音
  • 区分假名

比如在SQL Server 2005中,排序规则名称由两部份构成,比如 Chinese_PRC_CI_AI_WS 
前半部份是指本排序规则所支持的字符集,如Chinese_PRC 指针对大陆简体字UNICODE的排序规则。 
后半部份即后缀的含义如下:

 

_BIN                 指定使用向后兼容的二进制排序顺序。
_BIN2      指定使用 SQL Server 2005 中引入的码位比较语义的二进制排序顺序。
_Stroke   按笔划排序
_CI(CS) 是否区分大小写,CI不区分,CS区分
_AI(AS) 是否区分重音,AI不区分,AS区分
_KI(KS) 是否区分假名类型,KI不区分,KS区分
_WI(WS) 是否区分全半角,WI不区分,WS区分

 

既然排序规则如此复杂,那么应用了不同排序规则的列之间默认情况下便不能进行Union、Join、Like等equal操作了,于是便有了排序规则(collation)冲突。

排序规则(collation)冲突

我们知道,SQL Server 从2000 开始,便支持多个排序规则。SQL Server 2000 的数据库可使用除默认排序规则以外的其他排序规则。此外,SQL Server 2000 还支持为列专门制定排序规则。

这样一来,我们在写跨表、跨数据库、跨服务器操作的T-SQL时,如果equal的字段排序规则不同,便会发生排序规则冲突。

比如我们先见两个结构相同的表,但字段的排序规则不同:

 

  1. -- 1. Create TableA. 
  2.            CREATE TABLE TagsTableA 
  3.            ( 
  4.                TagName        NVARCHAR(64)    COLLATE Chinese_PRC_BIN 
  5.            )    
  6.            -- 2. Create TableB. 
  7.            CREATE TABLE TagsTableB 
  8.            ( 
  9.                TagName        NVARCHAR(64)    COLLATE Chinese_PRC_CI_AS 
  10.            )    

当表建好之后执行:

  1. -- 3. Try to join them 
  2.             SELECT * from TagsTableA A INNER JOIN TagsTableB B on A.TagName = B.TagName  

便会出下类似下面的问题

无法解决 equal to 操作中 "Chinese_PRC_BIN" 和 "Chinese_PRC_CI_AS" 之间的排序规则冲突。

常见的场景——临时表

我们知道,SQL Server的临时表是保存在Tempdb数据库中的。而使用临时表的数据库与临时表的排序规则(conllation)不一定相同。所以,当Tempdb的排序规则与当前使用临时表的数据库排序规则不同时,便会出现排序规则冲突。

一般来说,我们在创建临时表时可能不会注意到排序规则,从而留下排序规则冲突的隐患。

比如Openlab V4.0的Blog模块中的一个存储过程,便有着这种隐患:

  1. /****** 对?象ó:  StoredProcedure [blogs].[up_CreateGetTagIds]    脚本日期: 01/20/2010 19:10:32 ******/ 
  2. SET ANSI_NULLS ON 
  3. GO 
  4. SET QUOTED_IDENTIFIER ON 
  5. GO 
  6.  
  7. /* 
  8. RETURN VALUES
  9.     Ids 
  10. */ 
  11. -- ============================================= 
  12. -- Author:        <Lance Zhang> 
  13. -- Create date: <2010-01-06> 
  14. -- Description: <Make sure all the tag EXISTS in DB, and then get their ids.> 
  15. -- 1. Create Temp Table. 
  16. -- 2. Insert TagNames into Temp Table. 
  17. -- 3. Add new Tags to [Categories] from query Temp Table. 
  18. -- 4. Batch Get All Tag Ids from [Categories]. 
  19. -- 5. Clear and drop Temp Table. 
  20. -- ============================================= 
  21. ALTER PROCEDURE [blogs].[up_CreateGetTagIds] 
  22.     @BlogId                INT
  23.     @TagNames            XML 
  24. AS 
  25. BEGIN 
  26.     /******************************* SET CONFIG *************************************************/ 
  27.     SET NOCOUNT ON 
  28.     SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
  29.     SET NUMERIC_ROUNDABORT OFF 
  30.  
  31.     /******************************* DECLARE VARIABLE *************************************************/ 
  32.  
  33.     /********************************BEGIN TRANSATION**********************************************/ 
  34.  
  35.     BEGIN TRY 
  36.  
  37.         BEGIN TRANSACTION
  38.  
  39.             -- 1. Create Temp Table. 
  40.             CREATE TABLE #TagsTable 
  41.             ( 
  42.                 TagName        NVARCHAR(64)     
  43.             )    
  44.  
  45.             -- 2. Insert TagNames into Temp Table. 
  46.             INSERT INTO  
  47.                 #TagsTable  
  48.             SELECT  
  49.                 TG.Tags.value('@i','NVARCHAR(64)'AS TagName 
  50.             FROM  
  51.                 @TagNames.nodes('/ts/t') TG(Tags)  
  52.  
  53.             -- 3. Add new Tags to [Categories] from query Temp Table. 
  54.             BEGIN 
  55.                 INSERT INTO  
  56.                     [Categories] 
  57.                ( 
  58.                     [BlogId] 
  59.                     ,[ParentId] 
  60.                     ,[CategoryType] 
  61.                     ,[CategoryName] 
  62.                     ,[LoweredCategoryName] 
  63.                     ,[Slug] 
  64.                     ,[LoweredSlug] 
  65.                     ,[Description] 
  66.                     ,[CreatedDateUtc] 
  67.                     ,[TotalEntities] 
  68.                     ,[SortOrder] 
  69.                     ,[State] 
  70.                 ) 
  71.                 SELECT 
  72.                     @BlogId, 
  73.                     0,                                    -- ParentId, 0 as default. 
  74.                     2,                                    -- CategoryType, 2 as Post Tag. 
  75.                     TT.TagName, 
  76.                     LOWER(TT.TagName), 
  77.                     TT.TagName,                            -- Slug, use CategoryName as default. 
  78.                     LOWER(TT.TagName),                    -- LoweredSlug, use LoweredCategoryName as default. 
  79.                     '',                                    -- Description, Empty as default. 
  80.                     GETUTCDATE(), 
  81.                     0,                                    -- TotalEntities, 0 as default. 
  82.                     1,                                    -- SortOrder of PostTags can always be 1. 
  83.                     1                                    -- State, 1 as Normal. 
  84.                 FROM 
  85.                     #TagsTable TT 
  86.                 WHERE 
  87.                     LOWER(TT.TagName) NOT IN  
  88.                     ( 
  89.                         SELECT  
  90.                             C.[LoweredCategoryName]  
  91.                         FROM  
  92.                             [Categories] C WITH( UPDLOCK, HOLDLOCK ) 
  93.                         WHERE  
  94.                             [BlogId] = @BlogId 
  95.                             AND [CategoryType] = 2        -- Post Tag.     
  96.                     ) 
  97.  
  98.             END 
  99.  
  100.             -- 4. Batch Get All Tag Ids from [Categories]. 
  101.             BEGIN 
  102.                 SELECT 
  103.                     [CategoryId] 
  104.                 FROM 
  105.                     [Categories] C WITH(NOLOCK) 
  106.                 JOIN 
  107.                     #TagsTable TT 
  108.                 ON  
  109.                     C.[LoweredCategoryName] = LOWER( TT.TagName ) 
  110.                 WHERE 
  111.                     C.[BlogId] = @BlogId  
  112.                     AND C.[CategoryType] = 2                -- Post Tag. 
  113.                     AND C.[State] = 1                        -- 1 as Normal status. 
  114.             END 
  115.  
  116.             -- 5. Clear and drop Temp Table. 
  117.             TRUNCATE TABLE  
  118.                 #TagsTable 
  119.             DROP TABLE  
  120.                 #TagsTable 
  121.  
  122.         COMMIT TRANSACTION
  123.         RETURN 1 
  124.  
  125.     END TRY 
  126.  
  127.     BEGIN CATCH 
  128.         IF XACT_STATE() <> 0 
  129.         BEGIN 
  130.             ROLLBACK TRANSACTION
  131.             RETURN -1 
  132.         END 
  133.     END CATCH 
  134. END 
  135. GO 

常见的解决方案


知道了什么是排序规则冲突,我们接下来分析冲突的解决方案,以数据库级别的排序规则为例,一般来说,解决方案有下面几种

把SQL实例删了重建 ——大多数情况下等于没说-_-|||
修改数据库的排序规则 
在T-SQL中使用COLLATE DATABASE_DEFAULT来解决冲突 ——接下来主要讨论这


COLLATE DATABASE_DEFAULT

Collate XXX 操作可以用在字段定义或使用时,它会将字段定义或转换成XXX 的排序规则格式。而Collate Database_Default 则会将字段定义或转换成当前数据库的默认排序规则,从而解决冲突。

比如在下面的代码中便使用了Collate Database_Default 来解决字段在equal操作中的排序规则冲突:

  1. Insert into Security.Report (Name
  2.               Select C.Path From SSRS.Catalog C  
  3.         Where C.Path Collate Database_Default Like @ReportPath + '/%'  
  4.               And C.Path Collate Database_Default Not In (Select Name From Security.Report R) 

当然,在创建临时表时若对字段定义加上Collate Database_Default ,也可以方便地解决潜在的排序规则冲突,比如上一节中提到的存储过程,只要做如下修改即可。

  1. -- 1. Create Temp Table. 
  2.             CREATE TABLE #TagsTable 
  3.             ( 
  4.                 TagName        NVARCHAR(64)    COLLATE DATABASE_DEFAULT 
  5.             )    

 

结束语

对于专业的SQLer来说,排序规则的应用场景还有很多,例如利用排序规则特点计算汉字笔划和取得拼音首字母等等,更多信息,请查阅MSDN文档:http://msdn.micro

posted @ 2012-12-03 16:40  withoutaword  阅读(245)  评论(0编辑  收藏  举报