文心春萌

厌倦了城市的喧嚣,向往美丽的草原!

« 恢复误删数据(SQL Server 2000)--Log Explorer LogExplore简介 »

无数据库日志文件恢复数据库方法两则


方法一

1.新建一个同名的数据库

2.再停掉sql server(注意不要分离数据库)

3.用原数据库的数据文件覆盖掉这个新建的数据库

4.再重启sql server

5.此时打开企业管理器时会出现置疑,先不管,执行下面的语句(注意修改其中的数据库名)

6.完成后一般就可以访问数据库中的数据了,这时,数据库本身一般还要问题,解决办法是,利用
数据库的脚本创建一个新的数据库,并将数据导进去就行了.

USE MASTER
GO

SP_CONFIGURE 'ALLOW UPDATES',1 RECONFIGURE WITH OVERRIDE
GO

UPDATE SYSDATABASES SET STATUS =32768 WHERE NAME='置疑的数据库名'
Go

sp_dboption '置疑的数据库名', 'single user', 'true'
Go

DBCC CHECKDB('置疑的数据库名')
Go

update sysdatabases set status =28 where name='置疑的数据库名'
Go

sp_configure 'allow updates', 0 reconfigure with override
Go

sp_dboption '置疑的数据库名', 'single user', 'false'
Go

方法二

误删除数据库日志文件,最终成功恢复的全部步骤:
设置数据库为紧急模式
停掉SQL Server服务;

把应用数据库的数据文件XXX_Data.mdf移走;

重新建立一个同名的数据库XXX;

停掉SQL服务;

把原来的数据文件再覆盖回来;

运行以下语句,把该数据库设置为紧急模式;

运行“Use Master

Go

sp_configure 'allow updates', 1

reconfigure with override

Go”

执行结果:

DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

已将配置选项 'allow updates' 从 0 改为 1。请运行 RECONFIGURE 语句以安装。

接着运行“update sysdatabases set status = 32768 where name = 'XXX'”

执行结果:

(所影响的行数为 1 行)

重启SQL Server服务;

运行以下语句,把应用数据库设置为Single User模式;

运行“sp_dboption 'XXX', 'single user', 'true'”

执行结果:

命令已成功完成。

做DBCC CHECKDB;

运行“DBCC CHECKDB('XXX')”

执行结果:

'XXX' 的 DBCC 结果。

'sysobjects' 的 DBCC 结果。

对象 'sysobjects' 有 273 行,这些行位于 5 页中。

'sysindexes' 的 DBCC 结果。

对象 'sysindexes' 有 202 行,这些行位于 7 页中。

'syscolumns' 的 DBCC 结果。

………

运行以下语句把系统表的修改选项关掉;

运行“sp_resetstatus "XXX"

go

sp_configure 'allow updates', 0

reconfigure with override

Go”

执行结果:

在 sysdatabases 中更新数据库 'XXX' 的条目之前,模式 = 0,状态 = 28(状态 suspect_bit = 0),

没有更新 sysdatabases 中的任何行,因为已正确地重置了模式和状态。没有错误,未进行任何更改。

DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

已将配置选项 'allow updates' 从 1 改为 0。请运行 RECONFIGURE 语句以安装。

重新建立另外一个数据库XXX.Lost;

DTS导出向导
运行DTS导出向导;

复制源选择EmergencyMode的数据库XXX,导入到XXX.Lost;

选择“在SQL Server数据库之间复制对象和数据”,试了多次,好像不行,只是复制过来了所有表结构,但是没有数据,也没有视图和存储过程,而且DTS向导最后报告复制失败;

所以最后选择“从源数据库复制表和视图”,但是后来发现,这样总是只能复制一部分表记录;

于是选择“用一条查询指定要传输的数据”,缺哪个表记录,就导哪个;

视图和存储过程是执行SQL语句添加的。
这样,XXX.Lost数据库就可以替换原来的应用数据库了。

posted @ 2007-03-03 15:18 御天六龙 阅读(8) | 评论 (0)编辑 收藏

2007年3月1日

 

说到数据库,我认为不能不先谈数据结构。1996年,在我初入大学学习计算机编程时,当时的老师就告诉我们说:计算机程序=数据结构+算法。尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主,但我还是深深赞同8年前老师的告诉我们的公式:计算机程序=数据结构+算法。面向对象的程序开发,要做的第一件事就是,先分析整个程序中需处理的数据,从中提取出抽象模板,以这个抽象模板设计类,再在其中逐步添加处理其数据的函数(即算法),最后,再给类中的数据成员和函数划分访问权限,从而实现封装。

  数据库的最初雏形据说源自美国一个奶牛场的记账薄(纸质的,由此可见,数据库并不一定是存储在电脑里的数据^_^),里面记录的是该奶牛场的收支账目,程序员在将其整理、录入到电脑中时从中受到启发。当按照规定好的数据结构所采集到的数据量大到一定程度后,出于程序执行效率的考虑,程序员将其中的检索、更新维护等功能分离出来,做成单独调用的模块,这个模块后来就慢慢发展、演变成现在我们所接触到的数据库管理系统(DBMS)——程序开发中的一个重要分支。

  下面进入正题,首先按我个人所接触过的程序给数据库设计人员的功底分一下类:
  1、没有系统学习过数据结构的程序员。这类程序员的作品往往只是他们的即兴玩具,他们往往习惯只设计有限的几个表,实现某类功能的数据全部塞在一个表中,各表之间几乎毫无关联。网上不少的免费管理软件都是这样的东西,当程序功能有限,数据量不多的时候,其程序运行起来没有什么问题,但是如果用其管理比较重要的数据,风险性非常大。
  2、系统学习过数据结构,但是还没有开发过对程序效率要求比较高的管理软件的程序员。这类人多半刚从学校毕业不久,他们在设计数据库表结构时,严格按照教科书上的规定,死扣E-R图和3NF(别灰心,所有的数据库设计高手都是从这一步开始的)。他们的作品,对于一般的access型轻量级的管理软件,已经够用。但是一旦该系统需要添加新功能,原有的数据库表差不多得进行大换血。
  3、第二类程序员,在经历过数次程序效率的提升,以及功能升级的折腾后,终于升级成为数据库设计的老鸟,第一类程序员眼中的高人。这类程序员可以胜任二十个表以上的中型商业数据管理系统的开发工作。他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率,而且其设计的数据库可拓展性较好,当用户需要添加新功能时,原有数据库表只需做少量修改即可。
  4、在经历过上十个类似数据库管理软件的重复设计后,第三类程序员中坚持下来没有转行,而是希望从中找出偷懒窍门的有心人会慢慢觉悟,从而完成量变到质变的转换。他们所设计的数据库表结构有一定的远见,能够预测到未来功能升级所需要的数据,从而预先留下伏笔。这类程序员目前大多晋级成数据挖掘方面的高级软件开发人员。
  5、第三类程序员或第四类程序员,在对现有的各家数据库管理系统的原理和开发都有一定的钻研后,要么在其基础上进行二次开发,要么自行开发一套有自主版权的通用数据库管理系统。

  我个人正处于第三类的末期,所以下面所列出的一些设计技巧只适合第二类和部分第三类数据库设计人员。同时,由于我很少碰到有兴趣在这方面深钻下去的同行,所以文中难免出现错误和遗漏,在此先行声明,欢迎大家指正,不要藏私哦8)

  一、树型关系的数据表
  不少程序员在进行数据库设计的时候都遇到过树型关系的数据,例如常见的类别表,即一个大类,下面有若干个子类,某些子类又有子类这样的情况。当类别不确定,用户希望可以在任意类别下添加新的子类,或者删除某个类别和其下的所有子类,而且预计以后其数量会逐步增长,此时我们就会考虑用一个数据表来保存这些数据。按照教科书上的教导,第二类程序员大概会设计出类似这样的数据表结构:

类别表_1(Type_table_1)
名称     类型    约束条件   说明
type_id
  int   无重复   类别标识,主键
type_name
   char(50) 不允许为空 类型名称,不允许重复
type_father int
不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值

  这样的设计短小精悍,完全满足3NF,而且可以满足用户的所有要求。是不是这样就行呢?答案是NOWhy

  我们来估计一下用户希望如何罗列出这个表的数据的。对用户而言,他当然期望按他所设定的层次关系一次罗列出所有的类别,例如这样:
总类别
  类别1
    类别1.1
      类别1.1.1
    类别1.2
  类别2
    类别2.1
  类别3
    类别3.1
    类别3.2
  ……

  看看为了实现这样的列表显示(树的先序遍历),要对上面的表进行多少次检索?注意,尽管类别1.1.1可能是在类别3.2之后添加的记录,答案仍然是N次。这样的效率对于少量的数据没什么影响,但是日后类型扩充到数十条甚至上百条记录后,单单列一次类型就要检索数十次该表,整个程序的运行效率就不敢恭维了。或许第二类程序员会说,那我再建一个临时数组或临时表,专门保存类型表的先序遍历结果,这样只在第一次运行时检索数十次,再次罗列所有的类型关系时就直接读那个临时数组或临时表就行了。其实,用不着再去分配一块新的内存来保存这些数据,只要对数据表进行一定的扩充,再对添加类型的数量进行一下约束就行了,要完成上面的列表只需一次检索就行了。下面是扩充后的数据表结构:

类别表_2(Type_table_2)
名称     类型    约束条件    说明
type_id
  int   无重复   类别标识,主键
type_name
   char(50) 不允许为空 类型名称,不允许重复
type_father int
不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer char(6)
限定3,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数

  按照这样的表结构,我们来看看上面例子记录在表中的数据是怎样的:

type_id type_name type_father type_layer
1
总类别 0 000000
2
类别1 1 010000
3
类别1.1 2 010100
4
类别1.2 2 010200
5
类别2 1 020000
6
类别2.1 5 020100
7
类别3 1 030000
8
类别3.1 7 030100
9
类别3.2 7 030200
10
类别1.1.1 3 010101
……

  现在按type_layer的大小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer

列出记录集如下:

type_id type_name type_father type_layer
1
总类别 0 000000
2
类别1 1 010000
3
类别1.1 2 010100
10
类别1.1.1 3 010101
4
类别1.2 2 010200
5
类别2 1 020000
6
类别2.1 5 020100
7
类别3 1 030000
8
类别3.1 7 030100
9
类别3.2 7 030200
……

  现在列出的记录顺序正好是先序遍历的结果。在控制显示类别的层次时,只要对type_layer字段中的数值进行判断,每2位一组,如大于0则向右移2个空格。当然,我这个例子中设定的限制条件是最多3层,每层最多可设99个子类别,只要按用户的需求情况修改一下type_layer的长度和位数,即可更改限制层数和子类别数。其实,上面的设计不单单只在类别表中用到,网上某些可按树型列表显示的论坛程序大多采用类似的设计。

  或许有人认为,Type_table_2中的type_father字段是冗余数据,可以除去。如果这样,在插入、删除某个类别的时候,就得对type_layer 的内容进行比较繁琐的判定,所以我并没有消去type_father字段,这也正符合数据库设计中适当保留冗余数据的来降低程序复杂度的原则,后面我会举一个故意增加数据冗余的案例。

  
  二、商品信息表的设计
  假设你是一家百货公司电脑部的开发人员,某天老板要求你为公司开发一套网上电子商务平台,该百货公司有数千种商品出售,不过目前仅打算先在网上销售数十种方便运输的商品,当然,以后可能会陆续在该电子商务平台上增加新的商品出售。现在开始进行该平台数据库的商品信息表的设计。每种出售的商品都会有相同的属性,如商品编号,商品名称,商品所属类别,相关信息,供货厂商,内含件数,库存,进货价,销售价,优惠价。你很快就设计出4个表:商品类型表(Wares_type),供货厂商表(Wares_provider),商品信息表(Wares_info)

商品类型表(Wares_type)
名称     类型    约束条件    说明
type_id
  int   无重复   类别标识,主键
type_name
   char(50) 不允许为空 类型名称,不允许重复
type_father int
不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer char(6)
限定3,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数

供货厂商表(Wares_provider)
名称     类型    约束条件    说明
provider_id int
  无重复   供货商标识,主键
provider_name char(100)
不允许为空 供货商名称

商品信息表(Wares_info)
名称     类型    约束条件    说明
wares_id int
  无重复   商品标识,主键
wares_name char(100)
不允许为空 商品名称
wares_type
   int 不允许为空           商品类型标识,和Wares_type.type_id关联
wares_info char(200)
允许为空 相关信息
provider int
不允许为空 供货厂商标识,和Wares_provider.provider_id关联
setnum int
初始值为1 内含件数,默认为1
stock int
初始值为0 库存,默认为0
buy_price money
不允许为空 进货价
sell_price money
不允许为空 销售价
discount money
不允许为空 优惠价

  你拿着这3个表给老板检查,老板希望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增加了一个haspicBOOL型字段,然后再建了一个新表——商品图片表(Wares_pic)

商品图片表(Wares_pic)
名称     类型    约束条件    说明
pic_id int
  无重复   商品图片标识,主键
wares_id int
不允许为空 所属商品标识,和Wares_info.wares_id关联
pic_address
  char(200) 不允许为空           图片存放路径

  程序开发完成后,完全满足老板目前的要求,于是正式启用。一段时间后,老板打算在这套平台上推出新的商品销售,其中,某类商品全部都需添加长度的属性。第一轮折腾来了……当然,你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslengthBOOL型字段,又建了一个新表——商品长度表(Wares_length)

商品长度表(Wares_length)
名称     类型    约束条件    说明
length_id int
  无重复   商品图片标识,主键
wares_id int
不允许为空 所属商品标识,和Wares_info.wares_id关联
length
  char(20) 不允许为空           商品长度说明

  刚刚改完没多久,老板又打算上一批新的商品,这次某类商品全部需要添加宽度的属性。你咬了咬牙,又照方抓药,添加了商品宽度表(Wares_width)。又过了一段时间,老板新上的商品中有一些需要添加高度的属性,你是不是开始觉得你所设计的数据库按照这种方式增长下去,很快就能变成一个迷宫呢?那么,有没有什么办法遏制这种不可预见性,但却类似重复的数据库膨胀呢?我在阅读《敏捷软件开发:原则、模式与实践》中发现作者举过类似的例子:7.3 “Copy”程序。其中,我非常赞同敏捷软件开发这个观点:在最初几乎不进行预先设计,但是一旦需求发生变化,此时作为一名追求卓越的程序员,应该从头审查整个架构设计,在此次修改中设计出能够满足日后类似修改的系统架构。下面是我在需要添加长度的属性时所提供的修改方案:

  去掉商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。

商品额外属性表(Wares_ex_property)
名称     类型    约束条件    说明
ex_pid int
  无重复   商品额外属性标识,主键
p_name char(20)
不允许为空 额外属性名称

商品额外信息表(Wares_ex_info)
名称     类型    约束条件    说明
ex_iid int
  无重复   商品额外信息标识,主键
wares_id int
不允许为空 所属商品标识,和Wares_info.wares_id关联
property_id
  int 不允许为空           商品额外属性标识,和Wares_ex_property.ex_pid关联
property_value char(200)
不允许为空 商品额外属性值

  在商品额外属性表(Wares_ex_property)中添加2条记录:
ex_pid p_name
1
商品图片
2
商品长度

  再在整个电子商务平台的后台管理功能中追加一项商品额外属性管理的功能,以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表(Wares_ex_property)中添加一条记录即可。不要害怕变化,被第一颗子弹击中并不是坏事,坏的是被相同轨道飞来的第二颗、第三颗子弹击中。第一颗子弹来得越早,所受的伤越重,之后的抵抗力也越强

三、多用户及其权限管理的设计
  开发数据库管理类的软件,不可能不考虑多用户和用户权限设置的问题。尽管目前市面上的大、中型的后台数据库系统软件都提供了多用户,以及细至某个数据库内某张表的权限设置的功能,我个人建议:一套成熟的数据库管理软件,还是应该自行设计用户管理这块功能,原因有二:
  1.那些大、中型后台数据库系统软件所提供的多用户及其权限设置都是针对数据库的共有属性,并不一定能完全满足某些特例的需求;
  2.不要过多的依赖后台数据库系统软件的某些特殊功能,多种大、中型后台数据库系统软件之间并不完全兼容。否则一旦日后需要转换数据库平台或后台数据库系统软件版本升级,之前的架构设计很可能无法重用。

  下面看看如何自行设计一套比较灵活的多用户管理模块,即该数据库管理软件的系统管理员可以自行添加新用户,修改已有用户的权限,删除已有用户。首先,分析用户需求,列出该数据库管理软件所有需要实现的功能;然后,根据一定的联系对这些功能进行分类,即把某类用户需使用的功能归为一类;最后开始建表:
  
功能表(Function_table)
名称     类型    约束条件   说明
f_id int
  无重复   功能标识,主键
f_name char(20)
不允许为空 功能名称,不允许重复
f_desc char(50)
允许为空 功能描述

用户组表(User_group)
名称     类型    约束条件   说明
group_id int
无重复 用户组标识,主键
group_name char(20)
不允许为空 用户组名称
group_power char(100)
不允许为空 用户组权限表,内容为功能表f_id的集合

用户表(User_table)
名称     类型    约束条件   说明
user_id int
无重复 用户标识,主键
user_name char(20)
无重复 用户名
user_pwd char(20)
不允许为空 用户密码
user_type int
不允许为空 所属用户组标识,和User_group.group_id关联

  采用这种用户组的架构设计,当需要添加新用户时,只需指定新用户所属的用户组;当以后系统需要添加新功能或对旧有功能权限进行修改时,只用操作功能表和用户组表的记录,原有用户的功能即可相应随之变化。当然,这种架构设计把数据库管理软件的功能判定移到了前台,使得前台开发相对复杂一些。但是,当用户数较大(10人以上),或日后软件升级的概率较大时,这个代价是值得的。


  四、简洁的批量m:n设计
  碰到m:n的关系,一般都是建立3个表,m一个,n一个,m:n一个。但是,m:n有时会遇到批量处理的情况,例如到图书馆借书,一般都是允许用户同时借阅n本书,如果要求按批查询借阅记录,即列出某个用户某次借阅的所有书籍,该如何设计呢?让我们建好必须的3个表先:

书籍表(Book_table)
名称     类型    约束条件   说明
book_id int
无重复 书籍标识,主键
book_no char(20)
无重复 书籍编号
book_name char(100)
不允许为空 书籍名称
……

借阅用户表(Renter_table)
名称     类型    约束条件   说明
renter_id int
无重复 用户标识,主键
renter_name char(20)
不允许为空 用户姓名
……

借阅记录表(Rent_log)
名称     类型    约束条件   说明
rent_id int
无重复 借阅记录标识,主键
r_id int
不允许为空 用户标识,和Renter_table.renter_id关联
b_id int
不允许为空 书籍标识,和Book_table.book_id关联
rent_date datetime
不允许为空 借阅时间
……

  为了实现按批查询借阅记录,我们可以再建一个表来保存批量借阅的信息,例如:

批量借阅表(Batch_rent)
名称     类型    约束条件   说明
batch_id int
无重复 批量借阅标识,主键
batch_no int
不允许为空 批量借阅编号,同一批借阅的batch_no相同
rent_id int
不允许为空 借阅记录标识,和Rent_log.rent_id关联
batch_date datetime
不允许为空 批量借阅时间

  这样的设计好吗?我们来看看为了列出某个用户某次借阅的所有书籍,需要如何查询?首先检索批量借阅表(Batch_rent),把符合条件的的所有记录的rent_id字段的数据保存起来,再用这些数据作为查询条件带入到借阅记录表(Rent_log)中去查询。那么,有没有什么办法改进呢?下面给出一种简洁的批量设计方案,不需添加新表,只需修改一下借阅记录表(Rent_log)即可。修改后的记录表(Rent_log)如下:

借阅记录表(Rent_log)
名称     类型    约束条件   说明
rent_id int
无重复 借阅记录标识,主键
r_id int
不允许为空 用户标识,和Renter_table.renter_id关联
b_id int
不允许为空 书籍标识,和Book_table.book_id关联
batch_no int
不允许为空 批量借阅编号,同一批借阅的batch_no相同
rent_date datetime
不允许为空 借阅时间
……

  其中,同一次借阅的batch_no和该批第一条入库的rent_id相同。举例:假设当前最大rent_id64,接着某用户一次借阅了3本书,则批量插入的3条借阅记录的batch_no都是65。之后另外一个用户租了一套碟,再插入出租记录的rent_id68。采用这种设计,查询批量借阅的信息时,只需使用一条标准T_SQL的嵌套查询即可。当然,这种设计不符合3NF,但是和上面标准的3NF设计比起来,哪一种更好呢?答案就不用我说了吧。


  五、冗余数据的取舍
  上篇的树型关系的数据表中保留了一个冗余字段,这里的例子更进一步——添加了一个冗余表。先看看例子:我原先所在的公司为了解决员工的工作餐,和附近的一家小餐馆联系,每天吃饭记账,费用按人数平摊,月底由公司现金结算,每个人每个月的工作餐费从工资中扣除。当然,每天吃饭的人员和人数都不是固定的,而且,由于每顿工作餐的所点的菜色不同,每顿的花费也不相同。例如,星期一中餐5人花费40元,晚餐2人花费20,星期二中餐6人花费36元,晚餐3人花费18元。为了方便计算每个人每个月的工作餐费,我写了一个简陋的就餐记账管理程序,数据库里有3个表:

员工表(Clerk_table)
名称     类型    约束条件   说明
clerk_id int
无重复 员工标识,主键
clerk_name char(10)
不允许为空 员工姓名

每餐总表(Eatdata1)
名称     类型    约束条件   说明
totle_id int
无重复 每餐总表标识,主键
persons char(100)
不允许为空 就餐员工的员工标识集合
eat_date datetime
不允许为空 就餐日期
eat_type char(1)
不允许为空 就餐类型,用来区分中、晚餐
totle_price money
不允许为空 每餐总花费
persons_num int
不允许为空 就餐人数

就餐计费细表(Eatdata2)
名称     类型    约束条件   说明
id int
无重复 就餐计费细表标识,主键
t_id int
不允许为空 每餐总表标识,和Eatdata1.totle_id关联
c_id int
不允许为空 员工标识标识,和Clerk_table.clerk_id关联
price money
不允许为空 每人每餐花费

  其中,就餐计费细表(Eatdata2)的记录就是把每餐总表(Eatdata1)的一条记录按就餐员工平摊拆开,是个不折不扣的冗余表。当然,也可以把每餐总表(Eatdata1)的部分字段合并到就餐计费细表(Eatdata2)中,这样每餐总表(Eatdata1)就成了冗余表,不过这样所设计出来的就餐计费细表重复数据更多,相比来说还是上面的方案好些。但是,就是就餐计费细表(Eatdata2)这个冗余表,在做每月每人餐费统计的时候,大大简化了编程的复杂度,只用类似这么一条查询语句即可统计出每人每月的寄餐次数和餐费总帐:

SELECT clerk_name AS personname,COUNT(c_id) as eattimes,SUM(price) AS ptprice FROM Eatdata2 JOIN Clerk_tabsle ON (c_id=clerk_id) JOIN eatdata1 ON (totleid=tid) WHERE eat_date>=CONVERT(datetime,'"&the_date&"') AND eat_date<DATEADD(MONTH,1,CONVERT(DATETIME,'"&THE_DATE&"')) c_id

  想象一下,如果不用这个冗余表,每次统计每人每月的餐费总帐时会多麻烦,程序效率也够呛。那么,到底什么时候可以增加一定的冗余数据呢?我认为有2个原则:

  1、用户的整体需求。当用户更多的关注于,对数据库的规范记录按一定的算法进行处理后,再列出的数据。如果该算法可以直接利用后台数据库系统的内嵌函数来完成,此时可以适当的增加冗余字段,甚至冗余表来保存这些经过算法处理后的数据。要知道,对于大批量数据的查询,修改或删除,后台数据库系统的效率远远高于我们自己编写的代码。
  2、简化开发的复杂度。现代软件开发,实现同样的功能,方法有很多。尽管不必要求程序员精通绝大部分的开发工具和平台,但是还是需要了解哪种方法搭配哪种开发工具的程序更简洁,效率更高一些。冗余数据的本质就是用空间换时间,尤其是目前硬件的发展远远高于软件,所以适当的冗余是可以接受的。不过我还是在最后再强调一下:不要过多的依赖平台和开发工具的特性来简化开发,这个度要是没把握好的话,后期维护升级会栽大跟头的。

posted @ 2007-03-01 22:58 御天六龙 阅读(11) | 评论 (0)编辑 收藏

2007年2月28日

怀疑是其中的一个Sql Server日志文件(扩展名LDF)严重损坏,基本不能使用,但是数据文件损坏并不严重,我们通过下面的操作使数据全部恢复。
重启sql server服务。打开企业管理器,该数据库显示“置疑”,在查询分析器中执行下面的语句:

USE MASTER 
GO 
SP_CONFIGURE 'ALLOW UPDATES',1 RECONFIGURE WITH OVERRIDE 
GO 
UPDATE SYSDATABASES SET STATUS =32768 WHERE NAME='置疑的数据库名' 
Go 
sp_dboption '置疑的数据库名', 'single user', 'true' 
Go 
DBCC CHECKDB('置疑的数据库名') 
Go 
update sysdatabases set status =28 where name='置疑的数据库名' 
Go 
sp_configure 'allow updates', 0 reconfigure with override 
Go 
sp_dboption '置疑的数据库名', 'single user', 'false'
Go


用第三方工具

Log Explorer 3.2

http://www.ttdown.com/SoftDown.asp?ID=14562
http://www.yiii.net/app/servlet/net.yiii.club.DownloadServlet?Information_Id=I00023471
解压缩密码 www.heibai.net
http://www.ttdown.com/softview_8647.htm

Log Explorer for SQL Server v4.0.2
http://js.fixdown.com/soft/8324.htm

注册机产生的是注册码,是两个

用解压缩密码解开后,压缩包里也有一个注册机的

打开log explorer file=>attach log file->选择服务器和登陆方式->connect->
选择数据库->attach->左面对话框中browse->view log->就可以看到log记录了
想恢复的话: 右键log记录 undo transation->选择保存文件名和路径->然后打开该文件到查询分析器里执行
T-sql代码就可以了

例如 如果log是delete table where ...的话,生成的文件代码就是insert table ....

log explorer使用的几个问题

1)对数据库做了完全 差异 和日志备份
备份时选用了删除事务日志中不活动的条目
再用Log explorer打试图看日志时
提示No log recorders found that match the filter,would you like to view unfiltered data
选择yes 就看不到刚才的记录了


如果不选用了删除事务日志中不活动的条目
再用Log explorer打试图看日志时,就能看到原来的日志

2)修改了其中一个表中的部分数据,此时用Log explorer看日志,可以作日志恢复

3)然后恢复备份,(注意:恢复是断开log explorer与数据库的连接,或连接到其他数据上,
否则会出现数据库正在使用无法恢复)
恢复完后,再打开log explorer 提示No log recorders found that match the filter,would you like to view unfiltered data
选择yes 就看不到刚才在2中修改的日志记录,所以无法做恢复.


3)
不要用SQL的备份功能备份,搞不好你的日志就破坏了.

正确的备份方法是:
停止SQL服务,复制数据文件及日志文件进行文件备份.

然后启动SQL服务,用log explorer恢复数据


--------------------------------------------------------------------------------------
怎么样用数据库日志文件恢复数据库?
1:在企业管理器中恢复  
2:用sql语句恢复  
3:other  
---------------------------------------------------------------  

用数据库日志文件恢复数据库  
必需是在有完整備份的基礎上進行的      


1:在企业管理器中恢复:  
   所有工作中有一個retore  database  菜單  
   先進行完整備份數據的恢復  
   如果有差異備份可以同事恢復  

     然後再進行日志備份的數據恢復,選擇到某個時間進行恢復即可恢復到該時間點的數據。  

2:SQL  
   restore  database  res  from  disk='res.bak'    with  norecovery    (完整備份的恢復)  
RESTORE  log  res  from  disk='restran.bak'    with  stopat='2002/10/18  16:00'  



---------------------------------------------------------------  

我上面說的就是可以將數據庫恢復到某時間之前啊  

前提是你必需有完整備份  
如果你的完整備份是早上8:00做的  
然後你在12:00做了一次日志備份  
用以上語句你就可以恢復到8:00--12:00任何一個時刻的數據  


光有日志備份是沒有用的  
我想除非微軟的人自己才可以  


---------------------------------------------------------------  

恢复之前,要将DATABASE配置成"SINGLE  USE  "  
USE  MASTER  
GO  
SP_DBOPTION  'RES','single  user',true  
go  
restore  database  res  from  disk='res.bak'    with  norecovery  --(完整備份的恢復)  
go  
RESTORE  log  res  from  disk='restran.bak'    with  stopat='2002/10/18  16:00'  
go  
SP_DBOPTION  'RES','single  user',false  
go  

---------------------------------------------------------------  

我們來模擬一下好嗎  
假如每天早上8:00我們會為sql做一個完整備份  
中午12:00時候,你不小心將一個table的數據給幹掉了  

這個時候如果你隻是用8:00做的完整備份去恢復的話  
你隻能將數據恢復到8:00時候的數據,而8:00後的數據都將丟失  

但是如果你在12:00  馬上做一個日志備份  
這個時候你先用8:00  時候的完整備份做恢復  
然後又用日志備份去恢復8:00之後的數據  

假如你是12:00誤刪了數據,  
你可以用stopat=''2002/11/8  11:59'  的日志恢復方法  
將數據恢復到11:59的狀態  

而11:59的時候,你還沒有誤刪數據  
這正是我們要的效果
posted @ 2006-10-01 16:18 御天六龙 阅读(21) | 评论 (0)编辑 收藏
 

--引用
http://www.microsoft.com/china/technet/columns/tipsfortechs/wsqs3.asp
综合实例
下面给出了一个存储过程,它的作用是自动将当前数据库的用户存储过程加密。
DECLARE @sp_name nvarchar(400)
DECLARE @sp_content nvarchar(2000)
DECLARE @asbegin int
declare @now datetime
select @now = getdate()
DECLARE sp_cursor CURSOR FOR
SELECT object_name(id)
FROM sysobjects
WHERE xtype = 'P'
AND type = 'P'
AND crdate < @now
AND OBJECTPROPERTY(id, 'IsMSShipped')=0
OPEN sp_cursor
FETCH NEXT FROM sp_cursor
INTO @sp_name
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @sp_content = text FROM syscomments WHERE id = OBJECT_ID(@sp_name)
SELECT @asbegin = PATINDEX ( '%AS' + char(13) + '%', @sp_content)
SELECT @sp_content = SUBSTRING(@sp_content, 1, @asbegin - 1)
+ ' WITH ENCRYPTION AS'
+ SUBSTRING (@sp_content, @asbegin+2, LEN(@sp_content))
SELECT @sp_name = 'DROP PROCEDURE [' + @sp_name + ']'
EXEC sp_executesql @sp_name
EXEC sp_executesql @sp_content
FETCH NEXT FROM sp_cursor
INTO @sp_name
END
CLOSE sp_cursor
DEALLOCATE sp_cursor
该存储过程利用了 sysobjects 和 syscomments 表,并巧妙地修改了原存储过程的 SQL 定义语句,将 AS 修改为了 WITH ENCRYPTION AS,从而达到了加密存储过程的目的。本存储过程在 SQL Server 2000 上通过。

posted @ 2006-10-01 16:08 御天六龙 阅读(26) | 评论 (0)编辑 收藏
 

在论坛上经常看到有人问“如何实现数据的分类汇总”,很多的人都是介绍这样或那样的控件来实现,而没有从关系数据库语言(SQL)的本身来考虑实现方法。这里,我就借一个实例来说明如何借助SQL自身强大的功能来实现数据的分类汇总。

 

问题的提出:

 

现有表A,内容如下:

编码 仓库 数量
01 A 6
01 B 7
02 A 8
02 B 9

 

现在想按编码查询出这种格式:

编码 仓库 数量
01 A 6
01 B 7
汇总小计: 13
02 A 8
02 B 9
汇总小计: 17

 

问:该如何实现?

 

乍一看,好像很容易,用group by好像能实现?但仔细研究下去,你又会觉得group by也是无能为力,总欠缺点什么,无从下手。那么,到底该如何做呢?别急,SQL Server早就帮我们做好了,下面,跟我来。

 

首先,让我们来看一段话:

 

在生成包含小计和合计的报表时,ROLLUP 运算符很有用。ROLLUP 运算符生成的结果集类似于 CUBE 运算符所生成的结果集。

 

CUBE 运算符生成的结果集是多维数据集。多维数据集是事实数据的扩展,事实数据即记录个别事件的数据。扩展建立在用户打算分析的列上。这些列被称为维。多维数据集是一个结果集,其中包含了各维度的所有可能组合的交叉表格。

 

CUBE 运算符在 SELECT 语句的 GROUP BY 子句中指定。该语句的选择列表应包含维度列和聚合函数表达式。GROUP BY 应指定维度列和关键字 WITH CUBE。结果集将包含维度列中各值的所有可能组合,以及与这些维度值组合相匹配的基础行中的聚合值。

 

CUBE 和 ROLLUP 之间的区别在于: CUBE 生成的结果集显示了所选列中值的所有组合的聚合;ROLLUP 生成的结果集显示了所选列中值的某一层次结构的聚合。

 

看完以上的这段话,悟出了什么没有?如果没有,那么……嘿嘿,你的悟性还不够哟,离“三花棸顶”还早着呢:)。接下来我们再看一段(注意哟,答案马上就揭晓了):

 

SELECT 编码, 仓库, Sum(数量) as 数量FROM AGROUP BY 编码, 仓库 WITH ROLLUP--关键就是后面的WITH ROLLUP--当然,你也可以用WITH CUBE,但是结果会有点不大一样

 

可能看完上面这段你还是觉得“云里雾里”,摸不着头脑。实在不明白也没关系,自己动手做。

首先:建一个上面所说的A表,输入几行数据;接着:打开你的SQL Server查询分析器,连上包含你上面所建A表的服务器,选择包含该表的数据库;然后:Copy上面这段SQL 语句,Paste到查询分析器中,按F5,怎么样?看到下面出来了什么?是不是和我下面的一样?

编码 仓库 数量
01 A 6
01 B 7
01 NULL 13
02 A 8
02 B 9
02 NULL 17
NULL NULL 30

 

如果你用的是WITH CUBE,结果集的后面还会多出两条(如果你也只是输入示例中的几行数据的话):
NULL A 14
NULL B 16

 

咦!奇怪,结果中怎么有那么多“NULL”值?哈,别急,这几行正是我们所要的汇总数据行,不难看出:
01 NULL 13正是对编码为01的所有仓库中的数量的汇总;
02 NULL 17是对编码为02的所有仓库的数量的汇总;
NULL NULL 30是对所有资料行数量的汇总。

 

如何?答案出来了吧?是不是很简单呢?当然,上面还有点美中不足,那就是有好多“NULL”的存在。如何去掉这些无意义的NULL呢?下面我们再进行优化。

 

1、用Grouping替换NULL值

 

SELECT CASE WHEN (GROUPING(编码) = 1) THEN 'ALL'ELSE ISNULL(编码, 'UNKNOWN')END AS 编码,CASE WHEN (GROUPING(仓库) = 1) THEN 'ALL'ELSE ISNULL(仓库, 'UNKNOWN')END AS 仓库,SUM(数量) AS 数量FROM AGROUP BY 编码, 仓库 WITH ROLLUP--适当的运用Case函数

 

结果我这里就不写了,就是把上面的“NULL”值全部换成“ALL”字符串

 

2、利用程序做进一步的优化

 

//通常为了显示上的需要,我们必须对以上SQL语句生成的结果做一些优化,下面给出自然语言描述:WHILE(未到达最后一条记录){  IF 编码值不为ALL而仓库值为ALL  {     将编码值用“小计:”替换,将仓库值用""替换;     将这一行的颜色标示为灰色;  }  ELSE 编码值为ALL仓库值也为ALL  {     将编码值用“总计:”替换,将仓库值用""替换;     将这一行的着色标示为淡绿色;  }  指针移到下一条;}//当然,你尽可以发挥你的想象,把表格打扮得漂漂亮亮的,我就不再罗嗦了。

 

结束语:

 

通过上面的讲述,不知道你明白了没有,限于作者的文字表达能力,未解释清楚之处还请见谅。

  
  • 相关文章:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

本站采用创作共用版权协议, 要求署名、非商业用途和保持一致.

Auto Publisher Copyright Blog.cnxcn.net . All Rights Reserved.