文档库 最新最全的文档下载
当前位置:文档库 › 在数据库中使用对象的好处

在数据库中使用对象的好处

在数据库中使用对象的好处
在数据库中使用对象的好处

在数据库中使用对象的好处

我们都知道如何从Mysql获取我们需要的行(记录),读取数据,然后存取一些改动。很明显也很直接,在这个过程背后也没有什么拐弯抹角的。然而对于我们使用面对对象的程序设计(OOP)来管理我们数据库中的数据时,这个过程就需要大大改进一下了。这篇文章将对如何设计一个面对对象的方式来管理数据库的记录做一个简单的描述。你的数据当中的所有内部逻辑关系将被封装到一个非常条理的记录对象,这个对象能够提供专门(专一)的确认代码系统,转化以及数据处理。随着Zend Engine2 和PHP5的发布,PHP开发者将会拥有更强大的面对对象的工具来辅助工作,这将使这个过程(面对对象地管理数据库)更有吸引力。

以下列出了一些使用对象来描叙你的数据库的有利方面:

1、存取方法(Accessor methods)将会使你对属性的读取和写入过程做到完全的控制

2、每一级的每个记录和属性(的操作)都有确认过程

3、从关系表中智能的获取对象

4、重复使用的逻辑方法意味着所有的数据交互都要通过相同的基础代码(codebase),这将使维护变得更加简单

5、代码简单,因为不同的记录的内部逻辑都已经包含在各自所处的类(class)当中,而不是繁琐的库(lib)文件

6、在手工编写代码和SQL查询语句时,出错的机会将更少

存取方法(Accessor methods)

存取方式是通过类给实例(instance)的变量赋值。一个例子,我有一个叫User的类,并且有一个实例$username,我会写这样的存取方法(函数),User->username()和User->setUsername()用来返回和给实例赋值。

class User {

var $username;

function username() {

return $this->username;

}

function setUsername($newUsername) {

$this->username = $newUsername;

}

}

?>

这里有很好的理由让我们编写这样的“特别的代码”。它将使开发者更灵活的改变类的繁琐的工作,因为这一过程将不需要其他的使用类的php代码。让我们来看看下面这个更加完善的可信赖的User类。

变量$username将不复存在,所有的东西都被整合的放在数组$_data当中

如果username是空的话,username()函数将提供一个缺省(默认)的值给它

setUsername()过程将在接受值之前确认username是否合乎标准格式(如字长等)

class User {

var $_data = array(); // associative array containing all the attributes for the User

function username() {

return !empty($this->_data[\'username\']) ? $this->_data[\'username\'] : \'(no name!)\'; }

function setUsername($newUsername) {

if ($this->validateUsername($newUsername)) {

$this->_data[\'username\'] = $newUsername;

}

}

function validateUsername(&$someName) {

if (strlen($someName) > 12) {

throw new Exception(\'Your username is too long\

高性能的数据库

第一讲:范式设计

首先,俺说,数据库重在设计,然后才是开发。按照第三范式开发,会让你提升到一个新的境界!

名词解释:第三范式

第一范式:一个不包含重复列的表归于第一范式。

第二范式:如果一个表归于第一范式且只包含依赖于主键的列,则归于第二范式。

第三范式:如果一个表归于第二范式且只包含那些非传递性地依赖于主键的列,则归于第三范式。

chair3口述简单解释:

第一范式:不设计重复字段的表

比如:

Create Table tb1 (

fd1 varchar(20), --用来存放电话

fd2 varchar(20), --用来存放电话

fd3 int --其他

)

则fd1,fd2违反第一范式

第二范式:

第二范式:不设计没有主键,或没有唯一索引的表

比如:如果一个表存在相同的数据,那必然是违反第二范式无疑。

第三范式:能细分则细分每个字段。

比如:一个表,原来设计为:

Create TAble Clothes(

ClothesID int primary key,--ID

Color varchar(10), --颜色

Description varchar(20) --描述

)

那么Color违反了第三范式

于是,第三范式应该这样设计

Create TAble Clothes(

ClothesID int primary key,--ID

ColorID Int, --颜色ID

Description varchar(20) --描述

)

Create Table Color(

ColorID int primary key,

Color varchar(20)

)

Color作为主表,Clothes作为子表,两者用ColorID互联.

三范式设计的好处:减少数据冗余,提高系统可维护性,提高系统可扩展性。

三范式设计的缺点:会降低数据库的性能。(嘻嘻,不过非常少,大家放心)第二讲数据库编程

编程准则:(简单说说)

1.数据逻辑的放在一块

2.业务逻辑的放在一块

3.作界面的只做界面

4.最小网络传输

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

1、数据逻辑的放在一块

比如,表:History

那么,存储过程:spWriteHistory 表示数据逻辑:就是对History的写操作

同样,存储spQueryHistoryFromHistoryID表示根据HistoryID对History记录的查询

再同样,spQueryHistoryFromHistoryID;2(;不是普通的分号,是存储过程组)表示HistoryID 对History记录查询的另外一个类似的方法

2、业务逻辑

比如,还书,则spReturnBook

购买,则spBuyBook

此时可能要调用很多表,或很多数据逻辑:

Create Proc spBuyBook(

@iBookID int, --书ID

@iOperatorID int --操作员ID

)

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

--Name: spBuyBook

--Func: 购买一本书

--Var: 见上

--Use: spWriteOperateRecord,spProc2,spProc3....

--User: Chair3,Chair4

--Author: Chair3 # SapphireStudio (https://www.wendangku.net/doc/6514894708.html,)

--Date : 2003-4-16

--Memo : 临时写写的,给大家作个Sample

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

AS

Begin

Begin Tran

--数据逻辑:更新Book表,减少数量

Update Book

Set BookNumber=BookNumber-1

Where BookID=@iBookID

...

--业务逻辑:写入操作记录

Exec spWriteOperateRecord '某某人出售了一本书,书的ID为XXX'

...

Commit Tran

End

不要试图一个存储过程做完所有的事情,应当考虑将部分代码分为更小的存储过程,以达到代码公用以及可维护性。

当然,要求性能很高的部分,最好还是单独完成。

3、界面的程序不要直接调用数据表。

俺在赛格的时候,不允许其他同事直接读取表的,他们只能有调用Store Procedure 的权限. 这个根据开发环境而定,当然不是绝对,尽量如此.

这样,可以降低因为修改数据表而要对所有程序修改,检查的后果。

4、最小网络传输

4.1 减少使用Select * ....语句,选择你只需要的数据,Select Fd1,fd2 From tb1

4.2 减少客户端对数据库表的直接操作(与3有点同)

4.2 避免在客户端对数据库的循环写操作,把循环转移到服务器来.

4.3 ……忘了…靠经验了

4点最为重要,许多人的程序速度很慢,都是由于网络传输过多的缘故。

最后小结:优秀的程序员更重于对整体上的设计、可维护性、可扩展性的设计。

第三讲设计细节

到现在已经是第三讲了,也不知道听众几何……说得好的话,送之鲜花,说得不好的话,丢个鸡蛋把!好歹也让我chair3知道有几个人听了。

好,废话少说,now begin:

要点:

1.约束

2.默认值

3.计算字段

4.索引

以上乃数据库设计以及编程的最常用的部分了,下面听我一一将来

1、约束。

约束?何为约束?也就是对某一字段数值限定。以维护数据库数据的最党的纯洁性。一流的程序员打一开始,就应当知道某一字段的填写范围。

算了,理论不说了,举例子:

Create Table People (

Name varchar(20) Not Null, --姓名

Age int Not Null Check(Age>0) --年龄

)

大伙看了Age int Not Null Check(Age>0) ,中的Check(Age>0)就是防止用户不小心填写入<0的数值。哈哈,难道娘胎里的就算是-1岁么?

显然国务院没有如此规定。因此必须强迫Age>0。

2、默认值。

什么叫默认值不用我说了。数据表设计中,尽量避免Null的字段。采用默认值。

还是举例子有说服力!看:

Create Table People (

Name varchar(20) Not Null, --姓名

Sex bit Not Null Default 1, --性别

Age int Not Null Check(Age>0) --年龄

)

看到了没?Sex bit Not Null Default 1 ,性别,也就“男”或者“女”,用数字表示也就1 or 0 。在防止数据字段出现更多的情况(比如null),就必须使用not null。

照顾很多懒虫一般的客户(好像是说自己了),就给他默认一个“男”好了!唉,毕竟男女不打平等,很多地方都是男得多。(痛苦中…)

这里仅仅是举个例子,很多地方都可以用得到,比如日期之类的。请尽量避免null,而采用not null + default 能够更为纯洁你的数据库。

3、计算字段

优秀的设计人员,一开始就应当知道如何考虑到以后的使用的问题。比如在一个学生的表中(我这里是举个例子,实际上我不会写死subject的数量的)

Create Table Student(

StudentID int primary key ,

...

Chinese Float not null default 0,

English Float not null default 0,

Mathematics Float not null default 0

Sum As Chinese+English+Mathematics,

Average As (Chinese+English+Mathematics)/3,

....

)

相信这里聪明的人甚多,这个说些什么好呢?肚子有点饿…… 坚持一下,写完第4点马上作饭吃!

4、索引

这可是这里设计中的最最最最最最最最最最最最最最最最最最最最重要的部分!!

数据库的性能取决于索引的设计的好坏。

俺先给大家大致讲讲索引的种类:聚类索引,非聚类索引(Clustered Index and nonClustered index) 聚类索引通常创建于主键,主要创建于这些字段

1、主键、外建

2、返回某范围的数据

非聚类索引通常用于

1、乱糟糟的数据,很多都不一样D

2、而且数据经常要更改D

这样说大家似乎都不是很明白吧?

来吧,来吧,相约DevClu吧!

Create Table OperateRecord ( --操作记录

OperateRecordID int primary key, --流水号

OperatorID int not null, --操作员ID

Operation varchar(100), --操作内容

OperateDate DAteTime, --时间

Memo varchar(30) --备注

)

比如,在这里,如果经常要求对该操作员进行查询,那么OperatorID就应该采用聚类索引,如果还经常对操作内容进行查询,那么Operation就应该采用非聚类索引。

于是:

Create UNIQUE CLUSTERED Index idxOperateRecord_OperateREcordID On

OperateRecord(OperateREcordID)

Go

Create Index idxOperateRecord_Operation On OperateRecord(Operation)

Go

使用索引:

Select *

From OperateRecord W ith(Index=(idxOperateRecord_OperateREcordID)) --指定索引查询

Where OperateRecordID between 1 and 20000 --条件是OperateRecordID .

order by OperateRecordID

大家看明白了否?我肚子很饿了,没力气说了:(:(

试验证明,优秀的索引将大大提高查询的速度.

chair3以前曾经作个一个例子:

pIII (好像是500),128M Ram ,Win2K Server, SQLServer Enterprise

History表为1000万的数据。

1、使用聚类索引,我提取100万,耗时1分钟;提取1条,耗时2秒(我可能记错了,可能不用2秒的…我现在的机器都是一瞬间就提取出来了,不过我现在用P4,512内存。数据为1800万)

2、不使用任何索引(我把索引删掉),提取1条记录,耗时55秒;提取100万数据………我不敢,我怕死机。

当然也不是索引越多越好,索引越多,将会影响写的速度。一般说,一个表,有2-3个索引即可。根据实际情况

第四讲编程细节

1.触发器

2.游标

3.函数

4.存储过程

5.事务

1、触发器。

定义:何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一个特殊的存储过程。

常见的触发器有三种:分别应用于Insert , Update , Delete 事件。(SQL Server 2000定义了新的触发器,这里不提)

我为什么要使用触发器?比如,这么两个表:

Create Table Student( --学生表

StudentID int primary key, --学号

....

)

Create Table BorrowRecord( --学生借书记录表

BorrowRecord int identity(1,1), --流水号

StudentID int , --学号

BorrowDate datetime, --借出时间

ReturnDAte Datetime, --归还时间

...

)

用到的功能有:

1.如果我更改了学生的学号,我希望他的借书记录仍然与这个学生相关(也就是同时更改借书记录表的学号);

2.如果该学生已经毕业,我希望删除他的学号的同时,也删除它的借书记录。

等等。

这时候可以用到触发器。对于1,创建一个Update触发器:

Create Trigger truStudent

On Student

for Update

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

--Name:truStudent

--func:更新BorrowRecord 的StudentID,与Student同步。

--Use :None

--User:System

--Memo : 临时写写的,给大家作个Sample。没有调试阿。

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

As

if Update(StudentID)

begin

Update BorrowRecord

Set br.StudentID=i.StudentID

From BorrowRecord br , Deleted d ,Inserted i

Where br.StudentID=d.StudentID

end

理解触发器里面的两个临时的表:Deleted , Inserted 。注意Deleted 与Inserted分别表示触发事件的表“旧的一条记录”和“新的一条记录”。

一个Update 的过程可以看作为:生成新的记录到Inserted表,复制旧的记录到Deleted表,然后删除Student记录并写入新纪录。

对于2,创建一个Delete触发器

Create trigger trdStudent

On Student

for Delete

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

--Name:trdStudent

--func:同时删除BorrowRecord 的数据

--Use :None

--User:System

--Memo : 临时写写的,给大家作个Sample。没有调试阿。

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

As

Delete BorrowRecord

From BorrowRecord br , Delted d

Where br.StudentID=d.StudentID

从这两个例子我们可以看到了触发器的关键:A.2个临时的表;B.触发机制。

这里我们只讲解最简单的触发器。复杂的容后说明。

事实上,我不鼓励使用触发器。触发器的初始设计思想,已经被“级联”所替代。

2.游标

在SQL 2000之前,游标可谓是SQL Server心中的痛:老牛般的速度(CPU),河马般的胃口(内存)。可你却不能不用他。

什么叫游标呢?说白了就是像高级语言一样,是存放数据集,并逐条访问的一种机制。

比如在Delphi里面,要实现类似于这样的功能:(呵呵,不好意思,我只会Delphi,所以只能举一个Delphi的例子)

//这是一段Delphi的源代码

adoDataSet1.Close;

https://www.wendangku.net/doc/6514894708.html,mandText:=' Select * From Student order by StudentID ';

adoDataSet1.Open;

While Not adoDAtaSet1.Eof Do

Begin

YourVar:=adoDAtaSet1.FieldByName('StudentID').AsInteger;

DoSomeThing();

....

adoDataSet1.Next;

End

在SQL Server 并没有很好的数据逐条访问机制,如果有,那就是游标。

还是举例子:

对于表

Create Table BorrowRecord( --学生借书记录表

BorrowRecord int identity(1,1), --流水号

StudentID int , --学号

StudentFeeID int , --费用结算号(外键)

BorrowDate datetime, --借出时间

ReturnDAte Datetime, --归还时间

Fee Money --借书费用

...

)

Create Table StudentFee( --学生费用结算表

StudentFeeID int primarykey , --费用结算号(主键)

StudentID int , --学号

BorrowBookAllFee Money, --所有借书总费用

...

)

两者关系为多对一的关系,关联字段为StudentFeeID

由于某种原因StudentFee表的数据遭到了破坏,我想StudentFee循环一遍将“所有借书总费用”重算。

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

--Name:一部分代码

--func:更新学生借书总费用

--Use :

--User:

--Memo : 临时写写的,给大家作个Sample。没有调试阿。

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

--声明一个游标

Declare curStudentFee Cursor

for

Select StudentFeeID From StudentFee

--声明两个费用变量

Declare @mBorrowBookAllFee Money --总费用

Declare @iStudentFeeID Int --借书结算号

--初始化

Set @mBorrowBookAllFee=0

Set @iStudentFeeID=0

--打开游标

Open curStudentFee

--循环并提取记录

Fetch Next From curStudentFee Into @iStudentFeeID

While ( @@Fetch_Status=0 )

begin

--从借书记录中计算某一学生的借书总记录的总费用

Select @mBorrowBookA llFee=Sum(BorrowBookAllFee)

From BorrowRecord

Where StudentFeeID=@iStudentFeeID

--更新到汇总表。

Update StudentFee Set BorrowBookA llFee=@mBorrowBookA llFee

Where StudentFeeID=@iStudnetFeeID

Fetch Next From curStudentFee Into @mFee

end

--关闭游标

Close curStudentFee

--释放游标

Deallocate curStudentFee

关注游标的要点:1、声明、打开、关闭、释放;2、@@Fetch_Status 游标提取状态标志,0表示正确

这里,我也要提到,我不鼓励使用游标。更多的情况下,在SQL 2000 里面,函数已经能够实现绝大部分游标的功能。

3、函数。

函数是SQL 2000的新功能。一般的编程语言都有函数,我就不用解释函数是什么东东了。:)或许不少朋友会问:我用存储过程不就可以了么,我为什么要使用函数?

这里特别指出的一点:fn可以嵌套在Select语句中使用,而sp不可以。

这里不打算大批特批一番游标了,当然,在我的程序里面,基本抛弃了游标(这里特别说明,是“基本”!因为还是有很多地方费用导游表不可的。),转而采用了fn。游标太消耗资源了。受不了……我快要感动得要流泪了…

fn其实要比sp要简单得多。因为它的不确定性,从而也使他受到了不少的限制。

举个函数的小粒子:

Create Function fnTest ( @i int )

Returns bit

As

begin

Declare @b bit

if (Cast(@i As Float)/2)=(@i/2)

Set @b= 1

else

Set @b= 0

Return @b

end

Use the Sample:

Create Table #TT( fd1 int)

Declare @i int

Set @i=0

While @i<=20

begin

Insert Into #tt &#118alues(@i)

Set @i=@i+1

end

Select fd1,

'是否双数'=dbo.fnTest(fd1) --在这里调用了函数,注意哈:函数之前一定要加上他的owner. From #tt

Drop Table #tt

有了sp的编程基础,写fn也就不是什么很难的事情了。刚才我提到了,fn受到限制颇多,这里稍稍列举:

chair1. 只能调用确定性函数,不可以调用不确定函数。比如,不可以调用GetDate(),以及自己定义的不确定性函数。

chair2. 不可以使用动态SQL 。如:Execute, sp_ExecuteSQL (这是我最痛苦的事情了,痛哭中……)

chair3. 不可以调用扩展存储过程

chair4. 不可以调用Update语句对表进行更新

chair5. 不可以在函数内部创建表(Create TAble ),修改表(A lter TAble)

4、存储过程。

存储过程是数据库编程里面最重要的表现方式了。

呵呵,这里我要提到上次说道的:我拒绝使用触发器。这里我要开始猛批一顿触发器了。

在SQL 2000里,说实话,我实在找不出触发器可以存在的理由。回忆一下:触发器是一种特殊的存储过程。它在一定的事件(Insert,Update,Delete 等)里自动执行。我建议使用sp和级联来代替触发器。

在SQL 7 里面,触发器通常用于更新、或删除相关表的数据,以维护数据的完整。SQL 7里面,没有级联删除和级联修改的功能。只能建立起关系。既然SQL 2000里面提供了级联,那么触发器就没有很好的存在理由。更多的情况下是作为一个向下兼容的技术而存在。

当然,也有人喜欢把触发器作为处理数据逻辑,甚至是业务逻辑的自动存储过程。这种方法并不足取。这里列举以下使用触发器的一些坏处:

a、“地下”运行。

触发器没有很好的调试、管理环境。调试一段触发器,要比调试一段sp更耗费时间与精力。

b、类似于goto语句。(过分自由的另外一个说法是:无政府主义!)

一个表,可以写入多个触发器,包括同样for Update的10个触发器!同样for Delete的10个触发器。也就是说,你每次要对这个表进行写操作的时候,你要一个一个检查你的触发器,看看他们是做什么的,有没有冲突。

或许,你会很牛B的对我说:我不会做那么傻B的事情,我记得住我做了些什么!3个月以后呢?10个月以后呢?你还会对我说你记得住么?

c、嵌套触发器、递归触发器

你敢说你这么多的触发器中不会存在Table1更新了Table2表,从而触发Table2表更新TAble3,TAble3的触发器再次触发Table1更新Table2…… ??

或许还会发生这种情况:你的程序更新了Table1.Fd1,触发器立马更新Table1.fd1,再次触发事件,触发器再次更新Table1.fd1……

当然,SQL Server可以设置和避免应用程序进入死循环,可是,得到的结果,或许就不是你想要的。

……

我想不出触发器更多的坏处了,因为我早就抛弃了它。算了,不批它了,酸是各人爱好把!我建议使用完全存储过程来实现数据逻辑和事务逻辑!

先讲讲sp的编写格式(我个人的编程习惯)。良好的习惯有助于日后的维护。

Create Proc spBuyBook( --@@存储过程头,包括名字、参数、说明文档

@iBookID int, --书的ID --@@参数

@iOperatorID int --操作员ID

)

------------------------------------------------------- @@说明文档

--Name : spBuyBook @@名字

--func : 购买一本书的业务逻辑@@存储过程的功能

--Return: 0,正确;-1,没找到该书;-2,更新Book表出错;-3..... @@返回值解释

--Use : spDoSomething,spDoSomething2.... @@引用了那些外部程序,比如sp,fn,vw等

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

As --@@程序开始

begin

Begin Tran --@@激活事务

Exec spDoSomething --@@调用其他sp

if @@Error<>0 --@@判断是否错误

begin

Rollback Tran --@@回滚事务

RaisError ('SQL SERVER,spBuyBook: 调用spDoSomeThing发生错误。', 16, 1) with Log --@@记录日志

Return -1 --@@返回错误号

end

.... --更多其他代码

Commit Tran --@@提交事务

end

妈的我怎么这么背啊我??什么时候不死机,偏偏在这时!!丢了不少……:(:(

下面默哀3分钟……

1……

2……

3……

好了,继续!回忆刚才写的内容ing ……

AA、存储过程的几个要素: a. 参数 b.变量c.语句 d.返回值 e.管理存储过程

BB、更高级的编程要素: a.系统存储过程 b.系统表 c.异常处理 d.临时表 e.动态SQL f.扩展存储过程g.DBCC命令

AA.a 参数:知识要点包括:输入参数,输出参数,参数默认值

Sample:

Create Proc spTest(

@i int =0 , --输入参数

@o int output --输出参数

)

As

Set @o=@i*2 --对输出参数付值

Use the Sample:

Declare @o int

Exec spTest 33,@o output

Select @o --此时@o应该等于33*2=66。

AA.b 变量:AA.a中已经有声明变量的例子了,就是Declare @o int

AA.c 语句:在Sql Server 中,如果仅仅使用标准SQL语句将是不可想象的,通常认为,标准的SQL 语句就那么几条,如:

Select, Update, Delete

因此,我们需要引入更多更强大的功能,那就是T-SQL语句:

赋值语句:Set

循环语句:While

分支语句:if , Case ( Case语句不能单独使用,与一般高级语言的不同)

一起举个例子吧:

Sample :

Declare @i int

Set @i=0

While @i<100

begin

if @i<=20

begin

Select Case Cast(@i As Float)/2 When (@i/2) then Cast(@i As varchar(3)) + '是双数'

else Cast(@i As varchar(3)) + '是单数'

end

end

Set @i=@i+1

end

AA.d 返回值

Sample:

Create Proc spTest2

As

Return 22

Use the Sample

Declare @i int

Exec @i=spTest2

Select @i

AA.e 管理存储过程: 创建,修改,删除。

分别为:

Create Proc ... , Alter Proc ... , Drop Proc ...

BB、更高级的编程要素: a.系统存储过程 b.系统表 c.异常处理 d.临时表 e.动态SQL f.扩展存储过程g.DBCC命令

哈哈,以下课程收费!!(玩笑,实际上打算放到后面去讲了。)

5.事务

什么叫事务?这些就是数据库特有的术语了。懒虫在这里口头解释:就是把多件事情当做一件事情来处理。也就是大家同在一条船上,要活一起活,要over一起over !

我为什么要使用事务?俺这里再举个很俗很俗的例子:

俺到银行存钱,于是有这么几个步骤:

1、把钱交给工作人员;

2、工作人员填单;

3、将单子给我签字;

4、工作人员确认并输入电脑。

要是,要是我把钱交给工作人员之后,进行到3我签字了。那哥们突然心脏病发作,over掉了,那,我的钱还没有输入电脑,但我却交了钱又签字确认了,而并没有其他任何记录。我岂不是要亏死了???我的血汗钱啊!赶紧退给我!!

于是,在数据库里产生了这么一个术语:事务(Transaction),也就是要么成功,要么失败,并恢复原状。

还是写程序把:

Create Proc sp我去存款(@M Money , @iOperator Int)

As

Begin

Declare @i int

Begin Tran --激活事务

Exec @i=sp交钱@m,@iOperator

if @i<>0 --这里一般用系统错误号@@Error。我这里为了举例子没有用到。需要根据实际情况。begin

Rollback Tran --回滚事务

RaisError ('银行的窗口太少了,我懒得排队,不交了!:(', 16, 1) with Log --记录日志

Return -1 --返回错误号

end

Exec @i=sp填单@m,@iOperator

if @i<>0

begin

Rollback Tran --回滚事务

RaisError ('银行的哥们打印机出了点毛病,打印不出单子来,把钱退回来给我吧??', 16, 1) with

Log

Return -2

end

Exec @i=sp签字@m

if @i<>0

begin

Rollback Tran --回滚事务

RaisError ('我靠?什么烂银行,换了3支笔都写不出水来!!老子不存了!!不签!', 16, 1) with Log

Return -3

end

Exec @i=sp输入电脑@m,@iOperator

if @i<>0

begin

Rollback Tran --回滚事务

RaisError ('什么意思?磁盘空间已满?好了好了,把钱给我,我到旁边的这家银行!', 16, 1) with Log

Return -4

end

Commit Tran --提交事务

Return 0

End

事务的几个要点Begin Tran , @@Error(我这里没有用到,见上面的注释), Rollback Tran , Commit Tran。

另:事务可以嵌套使用。这个时候需要命名。请参见sql server online help 。

为数据库建立索引

就象许多的PHP开发者一样,在刚开始建立动态网站的时候,我都是使用相对简单的数据结构。PHP在连接数据库方面的确实是十分方便(译者注:有些人认为PHP在连接不同数据库时没有一个统一的接口,不太方便,其实这可以通过一些扩展库来做到这一点),你无需看大量的设计文档就可以建立和使用数据库,这也是PHP获得成功的主要原因之一。

前些时候,一位颇高级的程序员居然问我什么叫做索引,令我感到十分的惊奇,我想这绝不会是沧海一粟,因为有成千上万的开发者(可能大部分是使用MySQL的)都没有受过有关数据库的正规培训,尽管他们都为客户做过一些开发,但却对如何为数据库建立适当的索引所知较少,因此我起了写一篇相关文章的念头。

最普通的情况,是为出现在where子句的字段建一个索引。为方便讲述,我们先建立一个如下的表。

CREATE TA BLE mytable (

id serial primary key,

category_id int not null default 0,

user_id int not null default 0,

adddate int not null default 0

);

很简单吧,不过对于要说明这个问题,已经足够了。如果你在查询时常用类似以下的语句:SELECT * FROM mytable WHERE category_id=1;

最直接的应对之道,是为category_id建立一个简单的索引:

CREATE INDEX mytable_categoryid

ON mytable (category_id);

OK,搞定?先别高兴,如果你有不止一个选择条件呢?例如:

SELECT * FROM mytable WHERE category_id=1 AND user_id=2;

你的第一反应可能是,再给user_id建立一个索引。不好,这不是一个最佳的方法。你可以建立多重的索引。

CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);

注意到我在命名时的习惯了吗?我使用"表名_字段1名_字段2名"的方式。你很快就会知道我为什么这样做了。

现在你已经为适当的字段建立了索引,不过,还是有点不放心吧,你可能会问,数据库会真正用到这些索引吗?测试一下就OK,对于大多数的数据库来说,这是很容易的,只要使用EXPLAIN命令:

EXPLAIN

SELECT * FROM mytable

W HERE category_id=1 AND user_id=2;

This is what Postgres 7.1 returns (exactly as I expected)

NOTICE: QUERY PLA N:

Index Scan using mytable_categoryid_userid on

mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

以上是postgres的数据,可以看到该数据库在查询的时候使用了一个索引(一个好开始),而且它使用的是我创建的第二个索引。看到我上面命名的好处了吧,你马上知道它使用适当的索引了。接着,来个稍微复杂一点的,如果有个ORDER BY字句呢?不管你信不信,大多数的数据库在使用order by的时候,都将会从索引中受益。

SELECT * FROM mytable

W HERE category_id=1 AND user_id=2

ORDER BY adddate DESC;

有点迷惑了吧?很简单,就象为where字句中的字段建立一个索引一样,也为ORDER BY的字句中的字段建立一个索引:

CREATE INDEX mytable_categoryid_userid_adddate

ON mytable (category_id,user_id,adddate);

注意: "mytable_categoryid_userid_adddate" 将会被截短为

"mytable_categoryid_userid_addda"

CREATE

EXPLAIN SELECT * FROM mytable

W HERE category_id=1 AND user_id=2

ORDER BY adddate DESC;

NOTICE: QUERY PLA N:

Sort (cost=2.03..2.03 rows=1 width=16)

-> Index Scan using mytable_categoryid_userid_addda

on mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

看看EXPLAIN的输出,好象有点恐怖啊,数据库多做了一个我们没有要求的排序,这下知道性能如何受损了吧,看来我们对于数据库的自身运作是有点过于乐观了,那么,给数据库多一点提示吧。

为了跳过排序这一步,我们并不需要其它另外的索引,只要将查询语句稍微改一下。这里用的是postgres,我们将给该数据库一个额外的提示--在ORDER BY语句中,加入where语句中的字段。这只是一个技术上的处理,并不是必须的,因为实际上在另外两个字段上,并不会有任何的排序操作,不过如果加入,postgres将会知道哪些是它应该做的。

EXPLAIN SELECT * FROM mytable

W HERE category_id=1 AND user_id=2

ORDER BY category_id DESC,user_id DESC,adddate DESC;

NOTICE: QUERY PLA N:

Index Scan Backward using

mytable_categoryid_userid_addda on mytable

(cost=0.00..2.02 rows=1 width=16)

EXPLAIN

现在使用我们料想的索引了,而且它还挺聪明,知道可以从索引后面开始读,从而避免了任何的排序。

以上说得细了一点,不过如果你的数据库非常巨大,并且每日的页面请求达上百万算,我想你会获益良多的。不过,如果你要做更为复杂的查询呢,例如将多张表结合起来查询,特别是where 限制字句中的字段是来自不止一个表格时,应该怎样处理呢?我通常都尽量避免这种做法,因为这样数据库要将各个表中的东西都结合起来,然后再排除那些不合适的行,搞不好开销会很大。

如果不能避免,你应该查看每张要结合起来的表,并且使用以上的策略来建立索引,然后再用EXPLAIN命令验证一下是否使用了你料想中的索引。如果是的话,就OK。不是的话,你可能要建立临时的表来将他们结合在一起,并且使用适当的索引。

要注意的是,建立太多的索引将会影响更新和插入的速度,因为它需要同样更新每个索引文件。对于一个经常需要更新和插入的表格,就没有必要为一个很少使用的where字句单独建立索引了,对于比较小的表,排序的开销不会很大,也没有必要建立另外的索引。

以上介绍的只是一些十分基本的东西,其实里面的学问也不少,单凭EXPLAIN我们是不能判定该方法是否就是最优化的,每个数据库都有自己的一些优化器,虽然可能还不太完善,但是它们都会在查询时对比过哪种方式较快,在某些情况下,建立索引的话也未必会快,例如索引放在一个不连续的存储空间时,这会增加读磁盘的负担,因此,哪个是最优,应该通过实际的使用环境来检验。

在刚开始的时候,如果表不大,没有必要作索引,我的意见是在需要的时候才作索引,也可用一些命令来优化表,例如MySQL可用"OPTIMIZE TA BLE"。

综上所述,在如何为数据库建立恰当的索引方面,你应该有一些基本的概念了。

数据库设计中的14个技巧

1. 原始单据与实体之间的关系

可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单据对应多个实体,或多张原始单据对应一个实体。这里的实体可以理解为基本表。明确这种对应关系后,对我们设计录入界面大有好处。

〖例1〗:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表、社会关系表、工作简历表。这就是“一张原始单据对应多个实体”的典型例子。

2. 主键与外键

一般而言,一个实体不能既无主键又无外键。在E-R 图中, 处于叶子部位的实体, 可以定义主键,也可以不定义主键(因为它无子孙), 但必须要有外键(因为它有父亲)。

主键与外键的设计,在全局数据库的设计中,占有重要地位。当全局数据库的设计完成以后,有个美国数据库设计专家说:“键,到处都是键,除了键之外,什么也没有”,这就是他的数据库设计经验之谈,也反映了他对信息系统核心(数据模型)的高度抽象思想。因为:主键是实体的高度抽象,主键与外键的配对,表示实体之间的连接。

3. 基本表的性质

基本表与中间表、临时表不同,因为它具有如下四个特性:

(1) 原子性。基本表中的字段是不可再分解的。

(2) 原始性。基本表中的记录是原始数据(基础数据)的记录。

(3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。

(4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。

理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

4. 范式标准

基本表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。

〖例2〗:有一张存放商品的基本表,如表1所示。“金额”这个字段的存在,表明该表的设计不满足第三范式,因为“金额”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加“金额”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。

在Rose 2002中,规定列有两种类型:数据列和计算列。“金额”这样的列被称为“计算列”,而“单价”和“数量”这样的列被称为“数据列”。

表1 商品表的表结构

商品名称商品型号单价数量金额

电视机29吋2,500 40 100,000

5. 通俗地理解三个范式

通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;

第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;

第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。

没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是:在概念数据模型设计时

面向对象的数据库技术

面向对象的数据库技术 肖阳辉 摘要:面向对象的数据库极有可能是数据库发展的方向,关系型数据库已显得力不从心,面向对象技术已经渗透到了数据库领域,把面向对象的方法和数据库技术结合起来可以使数据库系统的分析、设计最大程度地与人们对客观世界的认识相一致。面向对象数据库的技术机理并不高深,但它的设计思想却极有价值。论文关键词:关,键,词,数据库,面向对象,技术 随着应用的日趋复杂和智能化,传统的关系数据库的缺点一点点的暴露出来,人们迫切希望产生一种新的数据库解决方案来适应这些复杂需求。一种新的解决方案呼之欲出。而这个解决方案极有可能就是面向对象数据库技术。面向对象数据库的技术机理并不高深,但它的设计思想却极有价值。在传统的面向对象应用开发中,由于传统的关系数据库开发风格完全不同于面向对象风格,使得许多程序员难以从复杂的SQL编程中解脱出来(尽管已经有一些成熟的ORM技术框架,如Hibernate,但程序员仍需要做大量的数据库代码工作),从而也无法从实质上提高工作效率。 1、面向对象数据库技术概述 面向对象是当前计算机界关心的重点,面向对象是一种新的方法学,也是一种认知方法学。它是一种支持模块化设计和软件重用的实际可行的编程方法,它把程序间的逻辑活动建立在对象间的消息传递之上,且设计上更加符合现实世界,更加自然,所以面向对象方法得到了更广泛的应用。 面向对象数据库系统是为了满足新的数据库应用需要而产生的新一代数据库系统。在数据库中提供面向对象的技术是为了满足特定应用的需要。随着许多基本设计应用(如MACD和ECAD)中的数据库向面向对象数据库的过渡,面向对象思想也逐渐延伸到其它涉及复杂数据的应用中,其中包括辅助软件工程(CASE)、计算机辅助印刷(CAP)和材料需求计划(MRP)。这些应用如同设计应用一样在程序设计方面和数据类型方面都是数据密集型的,它们需要识别于类型关系的存储技术,并能对相近数据备份进行调整。 还有许多应用要求多媒体数据库。它们要求以集成方式和文本或图形信息一起处理关系数据,这些应用包括高级办公室系统的其它文档管理系统。 面向对象数据库从面向程序设计语言的扩充着手使之成为基于面向对象程序设计语言的面向对象数据库。例如:ONTOS、ORION等,它们均是C++的扩充,熟悉C++的人均能很方便地掌握并使用这类系统。 面向对象数据库研究的另一个进展是在现有关系数据库中加入许多纯面向对象数据库的功能。在商业应用中对关系模型的面向对象扩展着重于性能优化,处理各种环境的对象的物理表示的优化和增加SQL模型以赋予面向对象特征。如UNISQL、O2等,它们均具有关系数据库的基本功能,采用类似于SQL的语言,用户很容易掌握。 2.面向对象数据库的优点 面向对象数据库是数据库技术与面向对象程序设计方法相结合的产物,由于同是面向对象方法学,所以其具有了所有面向对象的优点。同时,由于数据库主要操作的是集合(而不是单个数据),所以其又具有自身的特点和优点。 (1)提高数据库开发效率

面向对象数据模型

第三节面向对象数据模型 1、传统数据模型存在的主要问题 已于前述,目前非空间数据最主要的数据模型是层次模型、网状模型和关系模型。这里,我们分别介绍它们用于GIS地理数据库的局限性 (1)层次模型用于GIS地理数据库的局限性 层次模型反映了地理世界中实体之间的层次关系,在描述地理世界中自然的层次结构关系时简单、直观,易于理解,并在一定程度上支持数据的重构。它用于GIS地理数据库存在的主要问题是: 1)、很难描述复杂的地理实体之间的联系,描述多对多的关系时导致物理存储上的冗余; 2)、对任何对象的查询都必须从层次结构的根结点开始,低层次对象的查询效率很低,很难进行反向查询; 3)、数据独立性较差,数据更新涉及许多指针,插入和删除操作比较复杂,父结点的删除意味着其下层所有子结点均被删除; 4)、层次命令具有过程式性质,要求用户了解数据的物理结构,并在数据操纵命令中显式地给出数据的存取路径; 5)、基本不具备演绎功能和操作代数基础。 (2)网状模型用于GIS地理数据库的局限性 网状模型是层次模型的一般形式,反映了地理世界中常见的多对多关系,在一定程度上支持数据的重构,具有一定的数据独立和数据共享特性,且运行效率较高。用于GIS地理数据库的主要问题如下: 1)、由于网状结构的复杂性,增加了用户查询的定位困难,要求用户熟悉数据的逻辑结构,知道自己所处的位置; 2)、网状数据操作命令具有过程式性质,存在与层次模型相同的问题; 3)、不直接支持对于层次结构的表达; 4)、基本不具备演绎功能和操作代数基础。 (3)关系模型用于GIS地理数据库的局限性

关系模型表示各种地理实体及其间的关系,方式简单、灵活,支持数据重构;具有严格的数学基础,并与一阶逻辑理论密切相关,具有一定的演绎功能;关系操作和关系演算具有非过程式特点。尽管如此,关系模型用于GIS地理数据库也还存在一些不足。主要问题是: 1)、无法用递归和嵌套的方式来描述复杂关系的层次和网状结构,模拟和操作复杂地理对象的能力较弱; 2)、用关系模型描述本身具有复杂结构和涵义的地理对象时,需对地理实体进行不自然的分解,导致存储模式、查询途径及操作等方面均显得语义不甚合理; 3)、由于概念模式和存储模式的相互独立性,及实现关系之间的联系需要执行系统开销较大的联接操作,运行效率不够高。 不难看出,关系模型的根本问题是不能有效地管理复杂地理对象。 2、面向对象的概念 面向对象的基本概念是在本世纪70年代萌发出来的,它的基本做法是把系统工程中的某个模块和构件视为问题空间的一个或一类对象。到了80年代,面向对象的方法得到很快发展,在系统工程、计算机、人工智能等领域获得了广泛应用。但是,在更高级的层次上和更广泛的领域内对面向对象的方法进行研究还是90年代的事。 (1)基本思想和基本概念 面向对象的基本思想是通过对问题领域进行自然的分割,用更接近人类通常思维的方式建立问题领域的模型,并进行结构模拟和行为模拟,从而使设计出的软件能尽可能地直接表现出问题的求解过程。因此,面向对象的方法就是以接近人类通常思维方式的思想,将客观世界的一切实体模型化为对象。每一种对象都有各自的内部状态和运动规律,不同对象之间的相互联系和相互作用就构成了各种不同的系统。 在面向对象的方法中,对象、类、方法和消息是基本的概念。 对象——含有数据和操作方法的独立模块,可以认为是数据和行为的统一体。如一个城市、一棵树均可作为地理对象。对于一个对象,应具有如下特征: ·具有一个唯一的标识,以表明其存在的独立性; ·具有一组描述特征的属性,以表明其在某一时刻的状态; ·具有一组表示行为的操作方法,用以改变对象的状态。

面向对象数据库的应用范围和优缺点

面向对象数据库面向谁? 它的市场在哪里? 陶伟编译 当今冠以面向对象的东西很多,什么面向对象分析,面向对象设计,面向对象语言,面向对象操作系统,面向对象软件工程,等等,诸如此类,不胜枚举。似乎凡是和面对象挂上了边,就要好卖得多。其实面向对象也不是包治百病的灵丹妙药,数据库加上了面向对象同样不可能解决所有的问题,它不是大街上的冰棍,人人都可以买,它有自己独特的应用领域和特有的用户群。向不懂C++或Smalltalk的人兜售面向对象数据库,无异于对牛弹琴。 面向对象数据库的用户主要是从事系统软件和高级应用软件开发的程序员和系统设计人员,它不是大众数据库,在这方面它有别于关系数据库面。面向对象数据库好比是医生用的手术刀,干的是"细活儿",和家庭主妇用的菜刀(关系数据库)不一样,那是用来干"粗活儿"的。面向对象数据库是什么? 面向对象数据库=数据库系统+面向对象系统面向对象数据库必须满足两条准则:首先,它是一个数据库管理系统,其次它是一个面向对象系统。第一条准则可以翻译成六条特征:持久性、缓冲管理、数据共享、数据可靠性(事务管理和恢复)、即席查询以及模式修改。第二条准则可以翻译成:封装性、继承性、多态、对象标识、计算完整性、复杂对象和可扩展性。除了基本定义之外,为了满足所谓的非常规应用领域,还要引入一些扩展的特征:如版本管理、长事务、协同工作、触发和约束等。其中最重要的是封装性、继承性、多态,它们充分体现了面向对象的特征。面向对象数据库的应用领域面向对象数据库的应用面比较窄,主要集中于系统级的软件开发,以及数据密集的、复杂的应用软件开发,包括: ▲计算机辅助设计CAD ▲计算机辅助制造CAM ▲计算机集成制造系统CIMS ▲计算机辅助软件工程CASE ▲超大规模集成电路设计VLSI Design ▲地理信息系统GIS ▲决策支持系统DSS 这些应用领域和传统的商业应用领域有着显著不同,它们表现为复杂的数据类型,而不是简单的二维表,复杂的数据结构上定义着复杂的操作语义,需要不同层次的数据抽象能力,这些均是传统数据库系统所无法满足的。面向对象数据库与文件系统和关系数据库的区别文件系统存贮的数据格式因程序员的设计的不同而不同,一般不能保证数据的一致性和完整性,也很难多个用户共享。关系数据库存贮的是二维表,能保证数据的一致性与完整性,但只能用关系数据模型去描述世界。面向对象数据库则用面向对象数据模型去映射客观世界,存贮的不仅是数据,而且存贮了定义在数据上的操作语义,以及对象之间的复杂引用和约束关系。相对于文件系统和关系数据库而言,面向对象数据库的特点可以做以下的形象描述:面向对象数据库提供了一种存贮对象的更好方法,因为它提供了传统数据库的全部服务,然而却无存储和检索对象时对象的拆卸与装配的开销,比较关系数据库和面向对象数据库存储复杂对象,后者好似直接把汽车开进车库存放,而前者则是把汽车拆成零部件来保存。如何应用面向对象数据库面向对象数据已进入了商业应用领域,但没有取代现有的数据库系统,通常是在处理非常复杂的数据,或者数据很难被分解为关系数据库二维表的情况时,才采用面向对象数据库。一般认为图像数据的处理,使用面向对象数据库非常合适,其实还有许多数据类型可以用面向对象数据库来存储。就目前使用情况看,计算机辅助设计CAD是面向对象数据库最大的应用领域,现在电信、金融服务、医疗保健、制造过程控制等诸多领域,也开始推广使用。主要的面向对象数据库厂商主要的面向对象数据库厂商有以下几家。·Object Design,Inc. ·Gemstone System,Inc. ·O2 Technology,Inc. ·Objectivity,Inc. ·Versant Object Technology

面向对象的关系数据库设计

面向对象的关系数据库设计 2007-11-23 21:29 一、概念的区分 有些人把面向对象的数据库设计(即数据库模式)思想与面向对象数据库管理系统(OODBMS) 理论混为一谈。其实前者是数据库用户定义数据库模式的思路,后者是数据库管理程序的思路。用户使用面向对象方法学可以定义任何一种DBMS数据库,即网络型、层次型、关系型、面向对象型均可,甚至文件系统设计也照样可以遵循面向对象的思路。 面向对象的思路或称规范可以用于系统分析、系统设计、程序设计,也可以用于数据结构设计、数据库设计。OOSE自上至下、自始至终地贯彻面向对象思路,是一个一气呵成的统一体。面向对象的数据库设计只是 OOSE 的一个环节。 二、数据库设计的重要性 一般数据库设计方法有两种,即属性主导型和实体主导型。属性主导型从归纳数据库应用的属性出发,在归并属性集合(实体)时维持属性间的函数依赖关系。实体主导型则先从寻找对数据库应用有意义的实体入手,然后通过定义属性来定义实体。一般现实世界的实体数在属性数 1/10 以下时,宜使用实体主导型设计方法。面向对象的数据库设计是从对象模型出发的,属于实体主导型设计。 一般数据库应用系统都遵循以下相关开发步骤: 1 、设计应用系统结构; 2 、选择便于将应用程序与 DBMS 结合的DBMS体系结构,如RDBMS; 3 、根据应用程序使用的环境平台,选择适宜的DBMS(如Oracle)和开发工具(如PB); 4 、设计数据库,编写定义数据库模式的SQL程序; 5 、编写确保数据正确录入数据库的用户接口应用程序; 6 、录入数据库数据; 7 运行各种与数据库相关的应用程序,以确认和修正数据库的内容。 对以上各步骤,有几点需要说明: (1) 这不是瀑布模型,每一步都可以有反馈。以上各步不仅有反馈、有反复,还有并行处理。 比如一些库表在数据录入时,另一些库表设计还在修改。 这与我们的递增式开发方法有关,也与面向对象的特征有关。 (2) 上述顺序不是绝对的,大多数场合是从第三步开始的。 (3) 对大多数数据库应用系统来说,上述各步中最重要、最困难的不是应用系统设计而是数据库设 三、DBMS的支持和数据库设计 很多数据库应用系统开发者不重视数据库设计的原因是:他们太迷信DBMS,认为购入一个功能强大的 DBMS后数据库设计就不困难、不重要了。一些国内外的数据库教材常常是在为DBMS的开发厂商做宣传,而很少站在数据库用户角度,从数据库应用系统出发介绍数据库设计方法。结果往往使读者搞不清书中介绍的是数据库管理程序的设计思想,还是应用这种 DBMS 进行数据库设计的思想。 其实,DBMS只是给用户为已采用的数据库提供一个舞台,而是否使用这个舞台上

相关文档