冷月照霜城

憔悴江南倦客,不堪听、急管繁弦

博客园 首页 新随笔 联系 订阅 管理
今天帮一个新来的同事导数据库,结果遇到了一大堆的问题。。。
还好在我的一番努力和Baidu的帮助下,终于都搞定了。特此记录,以备后用。

问题1:无法连上对方的数据库
解决步骤:
  1. 在本机上,打开cmd窗口,用ping命令ping对方IP,结果正常;
  2. ping对方机器名,结果正常;
  3. 输入命令“telnet 对方IP 1433”,报错说无法连接到主机。
  4. 查看对方“服务端网络实用工具”设置,命名管道和TCP/IP都已启用,TCP/IP端口为1433;
  5. 检查对方SQL相关服务,发现已经启动
  6. 在对方计算机上打开telnet服务,关闭Windows自带防火墙。
  7. 在本机重新尝试telnet连接对方计算机的1433端口,依旧报错。
  8. 在对方计算机上打开cmd窗口,用“netstat -an”命令查看活动端口,未发现1433端口;
  9. 认定为未安装SQL Server 2000 SP4补丁, 安装补丁并重启SQL服务后1433端口成功打开。
  10. 在本机上再次telnet连接对方计算机,在屏幕左上角显示闪烁光标,表示连接成功。
  11. 在本机上新建SQL Server 注册,顺利完成。

  至此问题解决,提醒大家安装SQL Server后一定不要忘了打上相关的补丁。

问题2:无法完成导入 
  数据库连接成功之后,进行导入数据操作。
  新手需要注意的是在进行到“DTS导入/导出向导”的“指定表复制或查询”窗口时,如果想要把源数据库的主键、索引、存储过程之类的对象一并复制过来,这里不能按默认,要选择第3项:“在SQL Server数据库之间复制对象和数据”,否则仅能导入表结构和数据。
  结果第一次操作完成后显示导入失败,此时的源服务器为公司的一台应用服务器,检查发现在该服务器上我们要导入的源数据库中有不少表和存储过程的所有者不是dbo,而是以前别的开发人员建立的名为“ABC”的用户。而同事的SQL服务器并没有与之对应的登录和用户,所以在导入操作的最后会报错。
  所以我们在“DTS导入/导出向导”的“选择要复制的对象”这个窗口要把“使用默认选项”前面的勾取消掉,然后点后边的“选项”按钮,在“安全措施选项”里把第二项“复制SQL Server登录(Windows和SQL Server登录)”选中,然后“确定”,继续进行就可以了。
  最后成功的把服务器上的数据库导入到了同事的计算机上。
  其实这个步骤也可以通过“附加数据库”来实现,由于时间关系没有进行尝试。

问题3:导入后部分表和存储过程无法使用
  数据库导入完成后,同事在调试基于该库的程序时,发现频繁报错。提示为“无法找到表ABC.Product,对象不存在”之类的错误。
  查看同事修改后的该程序的连接字符串,发现是用SA登录,而此时数据库中有部分表和存储过程的所有者是ABC,出问题的正是这些对象。
  要更改这些对象的所有者,有两个系统存储过程可以使用:

  sp_changedbowner            //更改数据库所有者
  sp_changeobjectowner       //更改单个对象的所有者

  这里我们要改得是表和存储过程,所以用第二个就可以了。
  抄一段书:

语法

sp_changeobjectowner [ @objname = ] 'object' , [ @newowner = ] 'owner'

参数

[@objname =] 'object'

当前数据库中现有的表、视图或存储过程的名称。object 的数据类型为 nvarchar(517),没有默认值。object 可用现有对象所有者限定,格式为 existing_owner.object

[@newowner =] 'owner'

即将成为对象的新所有者的安全帐户的名称。owner 的数据类型为 sysname,没有默认值。owner 必须是当前数据库中有效的 Microsoft® SQL Server™ 用户或角色或 Microsoft Windows NT® 用户或组。指定 Windows NT 用户或组时,请指定 Windows NT 用户或组在数据库中已知的名称(用 sp_grantdbaccess 添加)。

返回代码值

0(成功)或 1(失败)

  但是问题又来了,该数据库有大量存储过程,难道要一个一个的在查询分析器中敲语句?
  逐个对象敲很难接受,上网BAIDU了一下,原来早已有高人写好存储过程,真是雪中送炭啊。
  新建一个存储过程,复制如下代码,保存:  

 1  --作者:不详   
 2  --成批更改Owner   
 3  --用法:exec   ChangeObjectOwner   'nmkspro','dbo'   
 4  --即可将所有nmkspro所有者的对象改为dbo所有   
 5  --运行成功后将提示:"注意:   更改对象名的任一部分都可能破坏脚本和存储过程。"   

 6  CREATE   PROCEDURE   dbo.ChangeObjectOwner   
 7  @OldOwner as   NVARCHAR(128),--参数原所有者   
 8  @NewOwner as   NVARCHAR(128)--参数新所有者   
 9  AS   
10    
11  DECLARE   @Name   as   NVARCHAR(128)     
12  DECLARE   @Owner as   NVARCHAR(128)   
13  DECLARE   @OwnerName as   NVARCHAR(128)   
14    
15  DECLARE   curObject   CURSOR   FOR     
16  select 'Name' =   name,   
17  'Owner' =   user_name(uid)   
18  from   sysobjects   
19  where   user_name(uid)=@OldOwner   
20  order   by   name   
21    
22  OPEN     curObject   
23  FETCH   NEXT   FROM   curObject   INTO   @Name,   @Owner   
24  WHILE(@@FETCH_STATUS=0)   
25  BEGIN           
26  if   @Owner=@OldOwner     
27  begin   
28  set   @OwnerName   =   @OldOwner   +   '.'   +   rtrim(@Name)   
29  exec   sp_changeobjectowner   @OwnerName,   @NewOwner   
30  end   
31    
32  FETCH   NEXT   FROM   curObject   INTO   @Name,   @Owner   
33  END   
34    
35  close   curObject   
36  deallocate   curObject   
37  GO 

  在查询分析器里执行: 

1USE databasename  --数据库名
2GO
3EXEC ChangeObjectOwner 'ABC','dbo'
4GO

 

  绝大多数的表和存储过程的所有者都被恢复成了“dbo”,只剩下一个名为“PlanDatas2”的表无法修改。

问题4:粗心带来的问题
  书接前文,好不容易把大部分东西搞定了,结果就剩下“PlanDatas2”这个“钉子户”,无论再怎么执行上问的存储过程,始终提示错误:  

服务器: 消息 15505,级别 16,状态 1,过程 sp_changeobjectowner,行 63
无法更改对象 'ABO.PlanDatas2' 或其所属的某个子对象的所有者,因为新所有者 'dbo' 已有同名的对象。

  看来对待“钉子户”还真不能用暴力手段,需要和平解决。去数据库中的表中仔细的观察了一下,没有发现名为“dbo.PlanDatas2”的表,这时想到一个偷懒的办法——把PlanDatas2改名,在企业管理器里将PlanDatas2重命名为“Dingzihu”,再运行ChangeObjectOwner存储过程,依然是同样的错误。
  仔细看错误提示,“……或其所属的某个子对象”,心中一动,虽然经过检查,数据库中肯定不存在与“dbo.Dingzihu”同名的表,但不等同于“ABC.Dingzihu”的子对象集中,必然不存在一个或多个“问题”子对象。这些“问题”子对象被修改所有者后的名称很有可能与其它表的子对象同名。
  由于有些隐藏的子对象在企业管理器中是看不到的,所以要打开查询分析器,展开左侧的用户表中的“ABC.Dingzihu”节点,显示出列、索引、约束、相关性、触发器等子对象,依次排查,结果发现——

  在“约束”中“主键约束”的名称居然是“ABC.PK_PlanDatas”,既不是“ABC.PK_Dingzihu”,也不是“ABC.PK_PlanDatas2”。

  我们知道,SQL Server默认为主键约束生成的名称是以“所有者.PK_表名”的格式来命名的。而剩下的对应其它列的约束是以“所有者.DF_表名_列名”的格式来命名的。这里的“表名”是指一开始创建主键时的表名,而在对表重命名后,约束名称并不会随之更新,依然保持原有的名称。
  也就是说,我在把表PlanDatas2改名为Dingzihu后,表的主键索引名称依然应该是“ABC.PK_PlanDatas2”,除非我手动修改或者是重新指定主键才会变化。
  但问题是怎么出来个“ABC.PK_PlanDatas”呢?应该是“ABC.PK_PlanDatas2”才对啊。
  这时发现,数据库中正是存在着另外一张名为dbo.PlanDatas的表!
  怪不得!用前面的存储过程修改所有者,当修改到“ABC.Dingzihu”表时,表名改成“dbo.Dingzihu”是没问题了,但进行到改主键约束名时,原来的“ABC.PK_PlanDatas”就变成了“dbo.PK_PlanDatas”,而这正是人家dbo.PlanDatas表的主键约束名称,人家当然不会让你改了,呵呵。
  发现了原因,解决起来就简单了,将“ABC.Dingzihu”的主键约束名称改成“ABC.PK_Dingzihu”,然后将对应其它列的默认约束(这些约束也是基于“PlanDatas”命名的)全部改名,如果是空约束则可全部删掉。然后再运行ChangeObjectOwner存储过程,OK,顺利完成,表名变成了“dbo.Dingzihu”。
  然后进到此数据库的“用户”中删除“ABC”这个用户,再到“安全性”下的“登录”选项中删除“ABC”这个登录,至此数据库完全回复正常。

思考:出现这个问题的原因,看来应该是当初使用ABC登录的开发人员一时粗心造成的。因为表1(PlanDatas)和表2(PlanDatas2)的结构基本相同,所以估计他是使用了“生成SQL脚本”功能生成了表1的脚本,然后简单改了一下TableName就用它来生成了表2,却忘记了修改主键约束名称。他可能没有想到,这小小的一点失误,却让后来维护者遇到了不小的麻烦。希望大家都能引以为鉴。

 

posted on 2008-02-28 17:03  青弦  阅读(2254)  评论(0编辑  收藏  举报