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

SQL SERVER无法解决is运算中排列规则冲突_排序规则

 

这些排序规则有什么作用呢?让我们先来看看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
    3.            ( 
    4. COLLATE
    5.            )    
    6. -- 2. Create TableB.
    7. CREATE TABLE
    8.            ( 
    9. COLLATE
    10.            )    
    当表建好之后执行:
    1. -- 3. Try to join them
    2. SELECT * from TagsTableA A INNER JOIN TagsTableB B on

    便会出下类似下面的问题

    无法解决 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
    22. ( 
    23. INT, 
    24.     @TagNames            XML 
    25. ) 
    26. AS
    27. BEGIN
    28. SET
    29. SET NOCOUNT ON
    30. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    31. SET NUMERIC_ROUNDABORT OFF
    32.  
    33. DECLARE
    34.  
    35. BEGIN
    36.  
    37. BEGIN
    38.  
    39. BEGIN TRANSACTION; 
    40.  
    41. -- 1. Create Temp Table.
    42. CREATE TABLE
    43.             ( 
    44.                 TagName        NVARCHAR(64)     
    45.             )    
    46.  
    47. -- 2. Insert TagNames into Temp Table.
    48. INSERT INTO
    49.                 #TagsTable  
    50. SELECT
    51. '@i','NVARCHAR(64)') AS
    52. FROM
    53. '/ts/t') TG(Tags)  
    54.  
    55. -- 3. Add new Tags to [Categories] from query Temp Table.
    56. BEGIN
    57. INSERT INTO
    58.                     [Categories] 
    59.                ( 
    60.                     [BlogId] 
    61.                     ,[ParentId] 
    62.                     ,[CategoryType] 
    63.                     ,[CategoryName] 
    64.                     ,[LoweredCategoryName] 
    65.                     ,[Slug] 
    66.                     ,[LoweredSlug] 
    67.                     ,[Description] 
    68.                     ,[CreatedDateUtc] 
    69.                     ,[TotalEntities] 
    70.                     ,[SortOrder] 
    71.                     ,[State] 
    72.                 ) 
    73. SELECT
    74.                     @BlogId, 
    75. -- ParentId, 0 as default.
    76. -- CategoryType, 2 as Post Tag.
    77.                     TT.TagName, 
    78. LOWER(TT.TagName), 
    79. -- Slug, use CategoryName as default.
    80. LOWER(TT.TagName),                    -- LoweredSlug, use LoweredCategoryName as default.
    81. '',                                    -- Description, Empty as default.
    82.                     GETUTCDATE(), 
    83. -- TotalEntities, 0 as default.
    84. -- SortOrder of PostTags can always be 1.
    85. -- State, 1 as Normal.
    86. FROM
    87.                     #TagsTable TT 
    88. WHERE
    89. LOWER(TT.TagName) NOT IN
    90.                     ( 
    91. SELECT
    92.                             C.[LoweredCategoryName]  
    93. FROM
    94. WITH( UPDLOCK, HOLDLOCK ) 
    95. WHERE
    96.                             [BlogId] = @BlogId 
    97. AND [CategoryType] = 2        -- Post Tag.    
    98.                     ) 
    99.  
    100. END
    101.  
    102. -- 4. Batch Get All Tag Ids from [Categories].
    103. BEGIN
    104. SELECT
    105.                     [CategoryId] 
    106. FROM
    107. WITH(NOLOCK) 
    108. JOIN
    109.                     #TagsTable TT 
    110. ON
    111. LOWER( TT.TagName ) 
    112. WHERE
    113.                     C.[BlogId] = @BlogId  
    114. AND C.[CategoryType] = 2                -- Post Tag.
    115. AND C.[State] = 1                        -- 1 as Normal status.
    116. END
    117.  
    118. -- 5. Clear and drop Temp Table.
    119. TRUNCATE TABLE
    120.                 #TagsTable 
    121. DROP TABLE
    122.                 #TagsTable 
    123.  
    124. COMMIT TRANSACTION; 
    125. RETURN
    126.  
    127. END
    128.  
    129. BEGIN
    130.         IF XACT_STATE() <> 0 
    131. BEGIN
    132. ROLLBACK TRANSACTION; 
    133. RETURN
    134. END
    135. END
    136. END
    137. 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
    3. Where C.Path Collate Database_Default Like @ReportPath + '/%'
    4. And C.Path Collate Database_Default Not In (Select Name From

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

    1. -- 1. Create Temp Table.
    2. CREATE TABLE
    3.             ( 
    4. COLLATE
    5.             )

     

    结束语

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