本文转自:http://ju.outofmemory.cn/entry/191163

问题描述

在SQL Server中使用一些复杂的存储过程时,我们需要借用临时表来完成一些逻辑的处理,例如:数据的临时存储、循环处理等等。 
临时表创建后,并不是在各个数据库中存在的,而是存在于系统数据库tempdb中。 
如今在一个包含临时表的存储过程中,我们遇到了SQL Collation冲突的错误:

错误信息:

Cannot resolve the collation conflict between "Chinese_PRC_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.

检查我们的应用数据库和tempdb的Collation,发现这两个数据库的Collation是不一致的。

应用数据库的Collation是:SQL_Latin1_General_CP1_CI_AS

tempdb的Collation是:Chinese_PRC_CI_AS

解决方法

通过GUI方式修改

tempdb作为系统的数据库,它的Collation是不可以被更改的,我们可以更改应用数据库的Collation来解决这个问题。

在数据库的Properties → Options有更改数据库的Collation选项,但修改时会出现错误:

通过SQL脚本修改

SQL脚本也可以修改数据库的Collation,但是要在SINGLE_USER WITH ROLLABACK IMMEDIATE模式下修改,修改完成后再还原为MULTI_USER模式

ALTER DATABASE SampleDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO ALTER DATABASE SampleDb COLLATE Chinese_PRC_CI_AS GO ALTER DATABASE SampleDb SET MULTI_USER
GO

执行SQL后,数据库的Collation变更为Chinese_PRC_CI_AS

]

验证Collation是否修改成功

要验证Collation是否修改成功,只需要再次执行存储过程,执行后仍然会出现Collation冲突错误:

Cannot resolve the collation conflict between "Chinese_PRC_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.

检查数据库中已创建的表字段的Collation属性,仍然是SQL_Latin1_General_CP1_CI_AS。

尝试在数据库中创建一个新的Table

CREATE TABLE Collation_Test
(
[ID] INT IDENTITY
PRIMARY KEY ,
[Name] NVARCHAR(30)
)
GO

查看Collation_Test表字段Name的Collation属性,则是Chinese_PRC_CI_AS

结论:这说明以上修改数据库Collation的SQL脚本,确实成功修改了数据库的Collation属性,新创建的表的字段的Collation属性会继承数据库的Collation属性,但它不会影响已创建的表的字段的Collation属性。 
简单地说,修改数据库的Collation,对于已存在的表没有影响,对于新创建的表有影响。

修改表字段的Collation属性

在表字段的属性中可以修改Collation属性,但这种方式每次只能更改一个,我们已经创建了很多的表,使用这种方式不太现实。

那么如何批量地修改表字段的Collation属性呢?

执行下面一段SQL可以解决这个问题:

DECLARE @collate NVARCHAR(100);
DECLARE @table NVARCHAR(255);
DECLARE @column_name NVARCHAR(255);
DECLARE @column_id INT;
DECLARE @data_type NVARCHAR(255);
DECLARE @max_length INT;
DECLARE @row_id INT;
DECLARE @sql NVARCHAR(MAX);
DECLARE @sql_column NVARCHAR(MAX); --期望的Collation
SET @collate = 'Chinese_PRC_CI_AS'; DECLARE local_table_cursor CURSOR
FOR
SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1 OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table WHILE @@FETCH_STATUS = 0
BEGIN DECLARE local_change_cursor CURSOR
FOR
SELECT ROW_NUMBER() OVER ( ORDER BY c.column_id ) AS row_id ,
c.name column_name ,
t.Name data_type ,
c.max_length ,
c.column_id
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id
AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID(@table)
ORDER BY c.column_id OPEN local_change_cursor
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id WHILE @@FETCH_STATUS = 0
BEGIN IF ( @max_length = -1 )
SET @max_length = 4000; IF ( @data_type LIKE '%char%' )
BEGIN TRY
SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN '
+ @column_name + ' ' + @data_type + '('
+ CAST(@max_length AS NVARCHAR(100))
+ ') COLLATE ' + @collate
PRINT @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR: Some index or constraint rely on the column'
+ @column_name + '. No conversion possible.'
PRINT @sql
END CATCH FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id END CLOSE local_change_cursor
DEALLOCATE local_change_cursor FETCH NEXT FROM local_table_cursor
INTO @table END CLOSE local_table_cursor
DEALLOCATE local_table_cursor GO

查看已存在的表的字段的Collation属性,已经成功修改为Chinese_PRC_CI_AS了。

并不是数据库中所有表字段的Collation都更改成功了,从输出结果中我们可以发现一些Error信息。

字符串类型(varchar,char,nchar,nvarchar..)主键字段的Collation没有被更改。

查询数据库中哪些表的字段Collation属性不是Chinese_PRC_CI_AS的字段:

DECLARE @collate SYSNAME
SELECT @collate = 'Chinese_PRC_CI_AS' SELECT '[' + SCHEMA_NAME(o.[schema_id]) + '].[' + o.name + '] -> ' + c.name ,
'ALTER TABLE [' + SCHEMA_NAME(o.[schema_id]) + '].[' + o.name + ']
ALTER COLUMN [' + c.name + '] ' + UPPER(t.name)
+ CASE WHEN t.name NOT IN ( 'ntext', 'text' )
THEN '('
+ CASE WHEN t.name IN ( 'nchar', 'nvarchar' )
AND c.max_length != -1
THEN CAST(c.max_length / 2 AS VARCHAR(10))
WHEN t.name IN ( 'nchar', 'nvarchar' )
AND c.max_length = -1 THEN 'MAX'
ELSE CAST(c.max_length AS VARCHAR(10))
END + ')'
ELSE ''
END + ' COLLATE ' + @collate
+ CASE WHEN c.is_nullable = 1 THEN ' NULL'
ELSE ' NOT NULL'
END
FROM sys.columns c WITH ( NOLOCK )
JOIN sys.objects o WITH ( NOLOCK ) ON c.[object_id] = o.[object_id]
JOIN sys.types t WITH ( NOLOCK ) ON c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE t.name IN ( 'char', 'varchar', 'text', 'nvarchar', 'ntext', 'nchar' )
AND c.collation_name != @collate
AND o.[type] = 'U'

删除主键

批量更改字段的Collation属性时,由于主键约束的存在,导致字符串类型的主键Collation无法被修改。 
但我们可以批量删除主键。

--包含主键的表名
DECLARE @tableName VARCHAR(100)
--动态SQL,用于DROP表的主键
DECLARE @sql NVARCHAR(1024) --定义游标
DECLARE curse CURSOR
FOR
--查询包含主键的表
SELECT T2.name
FROM sys.key_constraints T1 ,
sys.sysobjects T2
WHERE T1.type = 'PK'
AND T2.type = 'U'
AND CHARINDEX(t2.name, t1.name, 0) >= 1
--打开游标
OPEN curse;
FETCH NEXT FROM curse INTO @tableName; --循环游标
WHILE @@fetch_status = 0
BEGIN
--拼接SQL
SELECT @sql = 'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT '
+ name + ';'
FROM sys.key_constraints
WHERE [type] = 'PK'
AND [parent_object_id] = OBJECT_ID(@tableName);
--打印
PRINT @sql
--执行动态SQL
EXEC(@sql) FETCH NEXT FROM curse INTO @tableName;
END
--关闭游标
CLOSE curse;
--释放游标
DEALLOCATE curse;

删除主键后,再次执行批量更改字段Collation的语句。

总结

经过以上的反复折腾,我们终于将数据库以及表字段的Collation更改过来了,这是”事后“解决方案,并非最佳的解决方案。

更佳的解决方案应该是”事前“解决方案:

  1. 在开发开始之前,弄清楚客户的数据库环境,包括tempdb的collation和用户自定义数据库的默认collation。该项任务属于需求阶段的任务,但需要开发者的参与。
  2. 在开发环境准备时,尽量使用和客户环境一致的开发环境,包括服务器版本、服务器设定、数据库版本、数据库设定
  3. 开发开始时,先不要进行大规模的开发,而是全方位的再次确认开发环境。 
    对于本文中出现的问题,可以通过实施一些数据库的Test Case来解决。

参考

http://stackoverflow.com/questions/16730114/how-to-change-the-collate-to-all-the-columns-of-the-database

http://stackoverflow.com/questions/18122773/change-collations-of-all-columns-of-all-tables-in-sql-server

http://stackoverflow.com/questions/13948344/drop-primary-key-using-script-in-sql-server-database

 

[转]SQL Collation冲突解决 临时表的更多相关文章

  1. SQL Server中解决死锁

    SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...

  2. SQL Server中解决死锁的新方法介绍

    SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...

  3. Linq to Sql : 并发冲突及处理策略

    原文:Linq to Sql : 并发冲突及处理策略 1. 通过覆盖数据库值解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict) ...

  4. Linq to Sql并发冲突及处理策略

    0. 并发冲突的示例 单用户的系统现在应该比较罕见了,一般系统都会有很多用户在同时进行操作:在多用户系统中,涉及到的一个普遍问题:当多个用户“同时”更新(修改或者删除)同一条记录时,该如何更新呢?   ...

  5. mysql 主从,主主,主主复制时的主键冲突解决

    原理:slave 的i/o thread ,不断的去master抓取 bin_log, 写入到本地relay_log 然后sql thread不断的更新slave的数据 把主服务器所有的数据复制给从服 ...

  6. .Net中DLL冲突解决(真假美猴王)

    <西游记>中真假美猴王让人着实难以区分,但是我们熟知了其中的细节也不难把他们剥去表象分别出来.对问题不太关心的可以直接调到文中关于.Net文件版本的介绍 问题 最近在编译AKKA.net ...

  7. Git 分支管理和冲突解决

    Git 分支管理和冲突解决 创建分支 git branch 没有参数,显示本地版本库中所有的本地分支名称. 当前检出分支的前面会有星号. git branch newname 在当前检出分支上新建分支 ...

  8. Android Studio一些常用快捷键及快捷键冲突解决

    1. 最近在自学Android,也是边看书边写一些Demo,由于知识点越来越多,脑子越来越记不清楚,所以打算写成读书笔记,供以后查看,也算是把自己学到所理解的东西写出来,献丑,如有不对的地方,希望大家 ...

  9. IIS上虚拟站点的web.config与主站点的web.config冲突解决方法 分类: ASP.NET 2015-06-15 14:07 60人阅读 评论(0) 收藏

    IIS上在主站点下搭建虚拟目录后,子站点中的<system.web>节点与主站点的<system.web>冲突解决方法: 在主站点的<system.web>上一级添 ...

随机推荐

  1. hdu 1226 超级密码

    超级密码 Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem D ...

  2. fcitx、ibus、scim

    我觉得还是小企鹅fcitx好用点,兼容性好.速度快.配置简单. 在debian stable下,ibus用apt-get install完以后根本就不出现. scim倒是不用配置自己出来了,但是问题多 ...

  3. ASP.NET MVC5 高级编程-学习日记-第一章 入门

    1.1 ASP.NET MVC 简介 ASP.NET是一种构建Web应用程序的框架,它将一般的MVC(Model-View-Controller)模式应用于ASP.NET框架. 1.1.1 MVC模式 ...

  4. C# EPPlus导出EXCEL,并生成Chart表

    一  在negut添加EPPlus.dll库文件. 之前有写过直接只用Microsoft.Office.Interop.Excel 导出EXCEL,并生成Chart表,非常耗时,所以找了个EPPlus ...

  5. 背水一战 Windows 10 (60) - 控件(媒体类): Pointer 涂鸦板, InkCanvas 涂鸦板

    [源码下载] 背水一战 Windows 10 (60) - 控件(媒体类): Pointer 涂鸦板, InkCanvas 涂鸦板 作者:webabcd 介绍背水一战 Windows 10 之 控件( ...

  6. Loop List

    Loop List is very common in interview. This article we give a more strict short statement about its ...

  7. 深入字节码理解invokeSuper无限循环的原因

    来一段简单的cglib代码 public class SampleClass { public void test(){ System.out.println("hello world&qu ...

  8. centos7上编译安装mysql5.6

    注意,在做实验室统一关闭防火墙做的,在生产环境需要做防火墙规则的,大家要注意,做的时候尽量都是模仿生产环境的,比如服务一般都在/data/soft下面,尽量避免在/usr/local/下面. 安装编译 ...

  9. python 结巴分词简介以及操作

    中文分词库:结巴分词 文档地址:https://github.com/fxsjy/jieba 代码对 Python 2/3 均兼容 全自动安装:easy_install jieba 或者 pip in ...

  10. AutoCompleteTextView搭配Poi搜索实现多项选择

    项目需要 需要用到AutoCompleteTextView控件,在输入之后能在下方产生一个推荐结果的列表,就类似于金山词霸一类软件.输入一两个字符就能出来一系列类似的的单词, 这里做的例子是输入城市名 ...