| 楼主 |
2005-01-19 15:25:00 |
积分:0分
|
Microsoft Corporation
【「Microsoft SQL Server 7.0」以下简称「SQL Server 7.0」】
SQL Server 7.0 有哪几种安全模式?
两种:SQL Server和Windows NT(混合)及Windows NT only。SQL Server 和Windows NT相当于SQL Server 6.5版本的混合模式。Windows NT only相当于SQL Server 6.5 版本的信任模式。在SQL Server 7.0版没有提供相当于SQL Server 6.5版本的标准模式。
SQL Server 6.5的SQL安全管理器(Security Manger)为何在SQL Server 7.0不见了,替代工具是什么?
SQL Server 7.0提供了安全向导(Security Wizard)来取代SQL安全管理器(Security Manger)。
使用SQL Server和 Windows NT(混合)安全模式的优点是什么?
非Windows NT 客户端,Internet 客户端亦可登录 。
提供多层保护 。
使用Windows NT only安全模式的优点是什么?
用户可以更快速登录SQLServer,而无需另外再记一个密码。
使用NT提供的密码设置功能,如最小密码长度(minimum password length),..
只需为NT组设定帐号和组设置。
如何设置SQL Server 的安全模式?
启动SQL Server企业管理器(Enterprise Manager)。
在控制台(console)树中,展开Microsoft SQL Servers,然后展开SQL Server Group或您设定的组,单击选择要设定的SQL Server。
单击鼠标右键,选择"属性(Properties)",然后选择"安全(Security)"选项卡。
在Security部分选择"SQL Server and Windows NT"或是"Windows NT only" 。
如何在SQL Server企业管理器中,设置允许一个Windows NT组或用户能登录SQL Server?
在SQL Server企业管理器中,展开要加入Windows NT组或用户帐号的SQL Server,再展开Security 文件夹,选择 login,然后单击鼠标右键,选择快捷菜单中的new login。
选择"General"选项卡,在Name字段中填入"域\帐号"或"域\组"。
在Authentication(身份验证)部分选择Windows NT Authentication(Windows NT身份验证),然后选择domain (域)。
在Default(默认)部分选择预设数据库和语言。
单击"OK(确定)按钮。
如何使用SQL Server存储过程,使一个Windows NT组或用户能登录SQL Server?
sp_grantlogin {'login'}
其中'login" 包含域及组/用户帐号,不可超过128个字符。
设定Windows NT组或用户登录SQL Server时需要注意哪些事项?
如果SQL Server的登录id对应于一个Windows NT组,添加或删除Windows NT组中的用户不需重新建立SQL Server 的登录id。
如果SQL Server的登录id对应于一个Windows NT组或用户,将此Windows NT组或用户删除并不会将此SQL Server 的登录id删除。 |
回复
|
|
| 1楼 |
2005-01-19 15:25:00 |
积分:0分
|
其中,column是搜索列,使用“*”时说明对表中所有全文索引列进行搜索。Contains_search_ condition 说明CONTAINS语句的搜索内容,其语法格式为: {<simple_term>|<prefix_term>|<proximity_term>|<generation_term>|<weighted_term>}[{{AND|AND NOT|OR}<contains_search_condition>}] [...n] 下面就simple_term和prefix_term参数做简要说明: simple_term是CONTAINS语句所搜索的单字或短语,当搜索的是一个短语时,必须使用双引号作为定界符。其格式为: {‘word’|“ phrase”} prefix_term说明CONTAINS语句所搜索的字或短语前缀,其格式为: {“word*” | “phrase*”} 例如,下面语句检索Book表的Title列和Notes列中包含“database”或“computer”字符串的图书名称及其注释信息: select title, notes from book where contains(tilte, ‘database’) or contains(notes,‘database’) or contains(title,‘computer’) or contains(notes,‘computer’) FREETEXT语句的功能是在一个表的所有列或指定列中搜索一个自由文本格式的字符串,并返回与该字符串匹配的数据行。所以,FREETEXT语句所执行的功能又称做自由式全文查询。 FREETEXT语句的语法格式为:FREETEXT({column | * },‘freetext_string’) 其中,column是被搜索列,使用“*”时说明对表中的所有全文索引列进行搜索。Freetext_string参数指出所搜索的自由文本格式字符串。 例如,下面语句使用FREETEXT语句搜索Book表中包含“Successful Life”字符串的数据行: select title, notes from book where freetext(*,‘Successful Life’) |
回复
|
|
| 2楼 |
2005-01-19 15:25:00 |
积分:0分
|
SQL Server 2000中全文检索的使用(1)
引言 微软的SQL Server数据库是一个在中低端企业应用中占有广泛市场的关系型数据库系统,它以简单、方便、易用等特性深得众多软件开发人员和数据库管理人员的钟爱。但SQL Server 7.0以前的数据库系统由于没有全文检索功能,致使无法提供像文本内容查找此类的服务,成为一个小小的遗憾。从SQL Server 7.0起,到如今的SQL Server 2000终于具备了全文检索功能,使用户可以高效地检索存储在数据库char、varchar、text、ntext、nchar、nvarchar等数据类型列中的文本数据。 建立全文索引 在进行全文检索之前,必须先建立和填充数据库全文索引。为了支持全文索引操作,SQL Server 7.0新增了一些存储过程和Transact-SQL语句。使用这些存储过程创建全文索引的具体步骤如下(括号内为调用的存储过程名称): 1. 启动数据库的全文处理功能(sp_fulltext_ database);; 2. 建立全文检索目录(sp_fulltext_catalog); 3.在全文检索目录中注册需要全文索引的表(sp_fulltext_table); 4. 指出表中需要全文检索的列名(sp_fulltext_ column);; 5. 为表创建全文索引(sp_fulltext_table);; 6. 填充全文检索目录(sp_fulltext_catalog)。 下面举例说明如何创建全文索引,在本例中,对Test数据库Book表中Title列和Notes列建立全文索引。 use test //打开数据库 //打开全文索引支持,启动SQL Server的全文搜索服务 execute sp_fulltext_database ‘enable’ //建立全文检索目录ft_test execute sp_fulltext_catalog ‘ft_test’, ‘create’ 为Title列建立全文索引数据元,pk_title为Book表中由主键所建立的唯一索引,这个参数是必需的。 execute sp_fulltext_table ‘book’,‘create’, ‘ft_test’,‘pk_title’ //设置全文索引列名 execute sp_fulltext_column ‘book’, ‘title’, ‘add’ execute sp_fulltext_column ‘book’,‘notes’, ‘add’ //建立全文索引 execute sp_fulltext_table ‘book’, ‘activate’ //填充全文索引目录 execute sp_fulltext_catalog ‘ft_test’, ‘start_full’ 至此,全文索引建立完毕。 进行全文检索 SQL Server 2000提供的全文检索语句主要有CONTAINS和FREETEXT。CONTAINS语句的功能是在表的所有列或指定列中搜索:一个字或短语;一个字或短语的前缀;与一个字相近的另一个字;一个字的派生字;一个重复出现的字。 CONTAINS语句的语法格式为: CONTAINS({column | *}), <contains_search _condition> ) |
回复
|
|
| 3楼 |
2005-01-19 15:26:00 |
积分:0分
|
把拷贝过的SQL Server 7数据库恢复的正确方法 [www.Googln.com 2002-11-18 9:36:52 ] 在SQL Server 7中由于MS重新设计了数据库文件的存储方式,取消了新建设备再建数据库这一繁琐的过程。新的存储格式,一个数据库包括两个文件,mdf数据库文件和ldf日志文件。所以我们在重装机器备份时可以把你要备份的数据库的这两个文件拷贝出来,重新安装之后再恢复。
在SQL Server中提供了这种恢复方式的存储过程。
1.sp_attach_db [@dbname =] 'dbname',[@filename1 =] 'filename_n'
给系统添加一个数据库,在dbname指定数据库名称,filename_n指定数据库的文件和日志文件。比如我有一个voogiya的库,停止SQL Server服务备份voogiya_data.mdf,voogiya_log.ldf,启动SQL server,删除掉这个库,然后再把这两上文件拷到sql server DATA目录中,在Query Analyzer中执行如下语句:
EXEC sp_attach_db @dbname = N'voogiya',
@filename1 = N'd:\mssql7\data\voogiya_data.mdf',
@filename2 = N'd:\mssql7\data\voogiya_log.ldf'
就会把这个库加入到SQL Server Group中.
2.sp_attach_single_file_db [@dbname =] 'dbname',
[@physname =] 'physical_name'
这个命令和上面的功能一样,在physical_name中只要写上据库的物理文件名就可以了,日志文件SQL server会重新建立。这个存储过程的运行要先执行下面的存储过程:
sp_detach_db @dbname = 'dbname'
同样以上面的为例:
EXEC sp_detach_db @dbname = 'voogiya'
EXEC sp_attach_single_file_db @dbname = 'voogiya',
@physname = 'd:\mssql7\data\voogiya_data.mdf'
要注意执行以上存储过程的用户要在sysadmin中. 以上方法在windows Nt 4.0,service pack5,sql server 7.0上运行通过。 |
回复
|
|
| 4楼 |
2005-01-19 15:26:00 |
积分:0分
|
SQL Server 7.0数据库的六种数据移动方法(2) [www.Googln.com 2002-9-11 7:58:40 ] 5. 在应用程序中定制
可以在应用程序(PB、VB)中执行自己编写的程序,也可以在Query Analyzer中执行,这种方法比较灵活,其实是利用一个平台连接到数据库,在平台中用的主要时SQL语句,这种方法对数据库的影响小,但是如果用到远程链接服务器,要求网络之间的传输性能好,一般有两种语句:
1> select ... into new_tablename where ...
2> insert (into) old_tablename select ... from ... where ...
区别是前者把数据插入一个新表(先建立表,再插入数据),后者是把数据插入已经存在的一个表中,我个人喜欢后者,因为在编程的结构上,应用的范围上,第二条语句强于前者。
6. SQL Server的复制功能
SQL Server提供了强大的数据复制功能,也是最不易掌握的,具体应用请参考相关资料,值得注意的是要想成功进行数据的复制工作,有些条件是必不可少的:
1>SQL Server Agent必须启动,MSDTC必须启动。
2>所有要复制的表必须有主键。
3>如果表中有text或image数据类型,必须使用with log选项,不能使用with no_log选项。
另外max text repl size选项控制可以复制的文本和图像数据的最大规模,超过这个限制的操作将失败。
4>在要进行复制的计算机上,应该至少是隐含共享,即共享名是C$或D$…。
5>为SQL Server代理使用的Windows NT帐号不能是一个本地的系统帐号,因为本地的系统帐号不允许网络存取。
6>如果参与复制的服务器在另外的计算机域中,必须在这些域之间建立信任关系。 |
回复
|
|
| 5楼 |
2005-01-19 15:26:00 |
积分:0分
|
SQL Server 7.0数据库的六种数据移动方法(1) [www.Googln.com 2002-9-11 7:58:40 ] 本人从事的工作是数据库管理员,要维护多台服务器中的数据库,经常把某台服务器中的某个数据库移动到另外一台服务器,对数据的移动有些心得体会,希望和大家共同交流。
1. 通过工具DTS的设计器进行导入或导出
DTS的设计器功能强大,支持多任务,也是可视化界面,容易操作,但知道的人一般不多,如果只是进行SQL Server数据库中部分表的移动,用这种方法最好,当然,也可以进行全部表的移动。在SQL Server Enterprise Manager中,展开服务器左边的+,选择数据库,右击,选择All tasks/Import Data...(或All tasks/Export Data...),进入向导模式,按提示一步一步走就行了,里面分得很细,可以灵活的在不同数据源之间复制数据,很方便的。而且可以另存成DTS包,如果以后还有相同的复制任务,直接运行DTS包就行,省时省力。也可以直接打开DTS设计器,方法是展开服务器名称下面的Data Transformation Services,选Local Packages,在右边的窗口中右击,选New Package,就打开了DTS设计器。值得注意的是:如果源数据库要拷贝的表有外键,注意移动的顺序,有时要分批移动,否则外键主键,索引可能丢失,移动的时候选项旁边的提示说的很明白,或者一次性的复制到目标数据库中,再重新建立外键,主键,索引。
其实建立数据库时,建立外键,主键,索引的文件应该和建表文件分开,而且用的数据文件也分开,并分别放在不同的驱动器上,有利于数据库的优化。
2. 利用Bcp工具
这种工具虽然在SQL Server7的版本中不推荐使用,但许多数据库管理员仍很喜欢用它,尤其是用过SQL Server早期版本的人。Bcp有局限性,首先它的界面不是图形化的,其次它只是在SQL Server的表(视图)与文本文件之间进行复制,但它的优点是性能好,开销小,占用内存少,速度快。有兴趣的朋友可以查参考手册。
3. 利用备份和恢复
先对源数据库进行完全备份,备份到一个设备(device)上,然后把备份文件复制到目的服务器上(恢复的速度快),进行数据库的恢复操作,在恢复的数据库名中填上源数据库的名字(名字必须相同),选择强制型恢复(可以覆盖以前数据库的选项),在选择从设备中进行恢复,浏览时选中备份的文件就行了。这种方法可以完全恢复数据库,包括外键,主键,索引。
4. 直接拷贝数据文件
把数据库的数据文件(*.mdf)和日志文件(*.ldf)都拷贝到目的服务器,在SQL Server Query Analyzer中用语句进行恢复:
EXEC sp_attach_db @dbname = 'test',
@filename1 = 'd:\mssql7\data\test_data.mdf',
@filename2 = 'd:\mssql7\data\test_log.ldf'
这样就把test数据库附加到SQL Server中,可以照常使用。如果不想用原来的日志文件,可以用如下的命令:
EXEC sp_detach_db @dbname = 'test'
EXEC sp_attach_single_file_db @dbname = 'test',
@physname = 'd:\mssql7\data\test_data.mdf'
这个语句的作用是仅仅加载数据文件,日志文件可以由SQL Server数据库自动添加,但是原来的日志文件中记录的数据就丢失了。 |
回复
|
|
| 6楼 |
2005-01-19 15:27:00 |
积分:0分
|
dim obj3W
dim sServerName
set ObjService=GetObject("IIS://" & Request.ServerVariables("SERVER_NAME") & "/W3SVC")
for each obj3w in objservice
if IsNumeric(obj3w.Name) then
sServerName=Obj3w.ServerComment
if Instr(sServerName,"管理 WEB 站点") =0 then
sServerName=obj3w.Name & "-" & Obj3w.ServerComment
%>
<option value="<%=obj3w.Name%>"><%=sServerName%>
<%
end if
end if
next
set ObjService=Nothing
%>
</select>
</td>
</tr>
<tr>
<td width=40% align=center>
<font size=2>
请输入备份操作的虚拟目录名称:
</font>
</td>
<td>
<input Type=text name="vDir" value="SrrdBackUp" size=42>
</td>
</tr>
</table>
<br>
<br>
<input type=button Name="Build" value="生成虚拟目录">
<input type=button Name="Del" value="删除虚拟目录">
</form>
</center>
</body>
</html> <script language=vbs>
Sub Build_Onclick()
'生成临时虚拟目录
if Trim(document.BackForm.vDir.value)="" then
msgbox "请输入虚拟目录名称!",16,"错误"
exit sub
else
document.BackForm.action="BuildvDir.asp"
document.BackForm.submit
end if
end sub
Sub Del_Onclick()
'删除临时生成的虚拟目录
if Trim(document.BackForm.vDir.value)="" then |
回复
|
|
| 7楼 |
2005-01-19 15:27:00 |
积分:0分
|
Sql Server数据库备份的另类解决方案(1) [www.Googln.com 2002-11-18 9:39:26 ] 一、背景
一旦系统正常运行以后,系统维护最主要工作就是数据安全与可恢复性。本方案(以下提到的数据库均指微软的Sql Server7.0或以上数据库)主要探讨数据库备份与恢复。
一般的数据备份解决方案无非是以下三种:(1)、磁带备份;(2)、双机热备份;(3)、手工备份。作为一般的中小型政府部门和企业采用磁带备份,代价太高,性能价格比不高;普遍采用的可能是双机热备份方案,但是用户可能依然不放心,还需要手工备份,把数据存放到一个与外界断绝联系的可控环境中,这种情况是普通存在的。所以作为双机热备份方案的辅助方案或者在条件限制的情况下,作为双机热备份的替代方案,有必要整理出一套手工备份方案。 二、设计思路
Sql Server数据库本身提供非常方便强大的备份功能(DTS),可以以向导的方式引导用户备份到本地局域网的机器或者远程的机器上,但是现在出现一个问题:就是一旦数据库大了的话,本地局域网备份速度可以接受,可是远程备份,尤其是拨号上网,速度就可能慢,一旦时间过长,网络可能断掉,又得重新备份,能否提出一种方案充分利用Sql Server数据库本身已有的备份功能(DTS),同时又解决备份速度慢的问题,考虑到数据库备份文件的可压缩比率非常高,可以直接对备份文件进行压缩操作,是否更有效率?
下面是设计思路,最后定型取决于两种方式效率的高低。 第一步:利用Sql Server本身带有的备份功能(DTS)把数据库全部或者差额定时备份到某个目录,一旦备份成功,这时候在指定的备份目录下有.bak文件存在;
第二步:利用公司自开发的解压缩组件RichZip把.bak文件压缩成另一个文件.zip文件,RichZip的压缩比等同于WinZip;
第三步:通过Http协议下载.zip文件到本地,按照不同的项目和日期保存;
第四步:如果需要恢复,把.zip文件解压缩成.bak文件,然后再用Sql Server的工具把备份文件恢复; 需要实验解决的几个问题:
1、在同一环境下,直接使用Sql Server的备份工具与这种方案所需要的时间哪一个更长?是否在不同量级里面有不同的结果?
2、是自己利用组件开发一个基于http协议的下载程序,还是直接采用其它的共享下载工具是否更有效率?比如说NetAnts(网络蚂蚁)或者其它下载工具。 说明:
1、该方案只在微软平台上做过实际操作:操作系统Window NT4.0或者以上(推荐使用Win2000),数据库Sql Server6.5或者以上(推荐使用Sql7.0)。 三、实际情况 3.1 实验结果 3.1.1 实验一结果:无论在哪种连接环境下,局域网还是拨号上网,直接使用Sql Server的备份工具与本方案的效率差距都比较大,只是由于数据库小时,直接使用Sql Server的备份工具比本方案方便一些。 以下是一些简单的、不完整的实际操作数据,仅供参考。 操作环境:联想56K调制解调器,上网速度52K,通过SysGate上网,平均3K左右。 库名 备份文件大小 压缩文件大小 下载时间
Sql备份工具 本方案
SoftEnterPrise 27970Kb 2109Kb(13.26倍) 没有耐心等待,强行中断 12分钟
SoftProduct 10265Kb 739Kb(13.89倍) 18分钟 3分钟
SoftProductHz 11948Kb 930Kb(12.85倍) 25分钟 5分钟 |
回复
|
|
| 8楼 |
2005-01-19 15:28:00 |
积分:0分
|
SQL脚本生成的一些BUG(2) [www.Googln.com 2002-8-26 16:16:32 ] GO SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO Create function fnT2()
Returns Table
As
Return (Select * From vwT1) GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
-----------------------------------------------------------------------------
呵呵,一眼你就可以看出来了,建立view要比建立function先。而不是根据依赖关系建立……
毫无疑问,将会得到如下的错误:(这个错误可真严重!害得我好惨……)
---------------------------------------------------
服务器: 消息 208,级别 16,状态 1,过程 vwT1,行 4
对象名 'dbo.fnT1' 无效。
服务器: 消息 208,级别 16,状态 1,过程 fnT2,行 5
对象名 'vwT1' 无效。
---------------------------------------------------
2、作业脚本。 这个我就不说了,bug还不是很严重,主要是中文“--”注释符的问题,英文版我没有测试过,不过猜想应当没有这个bug。
大家可以试试看。 3、还有一个SP的问题。 大家看过我的精华里面有spGetIDStr和spAnalyseStrList了吧,关系是后者依赖于前者。可是spGetIDStr我并没有调用任何的表。
因此,每当运行Sql Server生成的脚本的时候,总是报告(大概是这样的信息):
------------------------------------------------------------------------
spGetIDStr并不存在,无法在sysdepends里建立依赖关系,存储过程spAnalyseStrList仍然建立。
--------------------------------------------------------------------------
无论我手工修改他的建立顺序还是什么的,用它生成的脚本就是有错。呵呵,这个破微软!
这里,再看看第三个bug,看我下面的测试程序:
(原理:当sp没有对表或视图等数据库对象有依赖关系的时候,sp被别的sp引用的时候也将无法建立依赖关系)
形成依赖关系:spB1 <--- spA1
------------------------------------------------------------------------
Create Proc spB1
As
Return 11 Go Create proc spA1
As
Begin
Declare @i int
Exec @i=spB1
Return @i*2
End
Go
-------------------------------------------------------------------------
生成的脚本就为:
------------------------------------------------------------------------- |
回复
|
|
| 9楼 |
2005-01-19 15:28:00 |
积分:0分
|
SQL脚本生成的一些BUG(1) [www.Googln.com 2002-8-26 16:16:32 ] 微软的SQL脚本生成令我伤透了心……我一直以为是我的程序上的问题,或者我操作上的失误,
并且,客服务人员屡屡埋怨我的程序bug,多次测试之后,靠,原来都是微软惹的祸…… Sql Server 的脚本生成有不少漏洞,经常由它生成的脚本运行起来却有错误。下面举例说明: 1、并没有根据sysdenpends的依赖关系生成SQL代码,而是根据“优先级”(呵呵,所谓的优先级)来生成。
比如:他认为view的优先级就要比function高。
那么,我写了下面的测试程序,形成如下的依赖关系:fnT1 <-- vwT1 <-- fnT2
就是,view vwT1处于依赖的中间。
------------------------------------
Create function fnT1()
Returns Integer
As
begin
Return 123
end
go Create view vwT1
As
Select aa=dbo.fnT1() Go Create function fnT2()
Returns Table
As
Return (Select * From vwT1)
Go
-------------------------------------
运行到数据库之后,用Enterprise生成SQL代码。(选项不一样,会有所不同,我没有选数据库和用户的)
-----------------------------------------------------------------------------------
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[fnT1]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[fnT1]
GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[fnT2]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[fnT2]
GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[vwT1]') and OBJECTPROPERTY(id, N'IsView') = 1)
drop view [dbo].[vwT1]
GO SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO Create view vwT1
As
Select aa=dbo.fnT1() GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO Create function fnT1()
Returns Integer
As
begin
Return 123
end GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON |
回复
|
|
| 10楼 |
2005-01-19 15:29:00 |
积分:0分
|
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[spA1]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[spA1]
GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[spB1]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[spB1]
GO SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
Create proc spA1
As
Begin
Declare @i int
Exec @i=spB1
Return @i*2
End GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO Create Proc spB1
As
Return 11
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO |
回复
|
|
| 11楼 |
2005-01-19 15:30:00 |
积分:0分
|
大型数据库设计原则
南昌航空工业学院电子工程系
喻金科
---- 一个好的数据库产品不等于就有一个好的应用系统,如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能。一般来讲,在一个MIS系统分析、设计、测试和试运行阶段,因为数据量较小,设计人员和测试人员往往只注意到功能的实现,而很难注意到性能的薄弱之处,等到系统投入实际运行一段时间后,才发现系统的性能在降低,这时再来考虑提高系统性能则要花费更多的人力物力,而整个系统也不可避免的形成了一个打补丁工程。笔者依据多年来设计和使用数据库的经验,提出以下一些设计准则,供同仁们参考。 命名的规范
---- 不同的数据库产品对对象的命名有不同的要求,因此,数据库中的各种对象的命名、后台程序的代码编写应采用大小写敏感的形式,各种对象命名长度不要超过30个字符,这样便于应用系统适应不同的数据库。
游标(Cursor)的慎用
---- 游标提供了对特定集合中逐行扫描的手段,一般使用游标逐行遍历数据,根据取出的数据不同条件进行不同的操作。尤其对多表和大表定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等特甚至死机,笔者在某市《住房公积金管理系统》进行日终帐户滚积数计息处理时,对一个10万个帐户的游标处理导致程序进入了一个无限期的等特(后经测算需48个小时才能完成)(硬件环境:Alpha/4000 128Mram ,Sco Unix ,Sybase 11.0),后根据不同的条件改成用不同的UPDATE语句得以在二十分钟之内完成。示例如下:
Declare Mycursor cursor for select count_no from COUNT
Open Mycursor
Fetch Mycursor into @vcount_no
While (@@sqlstatus=0)
Begin
If @vcount_no=’’ 条件1
操作1
If @vcount_no=’’ 条件2
操作2
。。。
Fetch Mycursor into @vcount_no
End
。。。
。。。
改为
Update COUNT set 操作1 for 条件1
Update COUNT set 操作2 for 条件2
。。。
。。。 ---- 在有些场合,有时也非得使用游标,此时也可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行操作,可时性能得到明显提高。笔者在某地市〈电信收费系统〉数据库后台程序设计中,对一个表(3万行中符合条件的30多行数据)进行游标操作(硬件环境:PC服务器,PII266 64Mram ,NT4.0 Ms Sqlserver 6.5)。 示例如下: Create #tmp /* 定义临时表 */
( 字段1
字段2
。。。
)
Insert into #tmp select * from TOTAL where
条件 /* TOTAL中3万行 符合条件只有几十行 */
Declare Mycursor cursor for select * from #tmp
/*对临时表定义游标*/
。。。 索引(Index)的使用原则
---- 创建索引一般有以下两个目的:维护被索引列的唯一性和提供快速访问表中数据的策略。大型数据库有两种索引即簇索引和非簇索引,一个没有簇索引的表是按堆结构存储数据,所有的数据均添加在表的尾部,而建立了簇索引的表,其数据在物理上会按照簇索引键的顺序存储,一个表只允许有一个簇索引,因此,根据B树结构,可以理解添加任何一种索引均能提高按索引列查询的速度,但会降低插入、更新、删除操作的性能,尤其是当填充因子(Fill Factor)较大时。所以对索引较多的表进行频繁的插入、更新、删除操作,建表和索引时因设置较小的填充因子,以便在各数据页中留下较多的自由空间,减少页分割及重新组织的工作。
数据的一致性和完整性
---- 为了保证数据库的一致性和完整性,设计人员往往会设计过多的表间关联(Relation),尽可能的降低数据的冗余。表间关联是一种强制性措施,建立后,对父表(Parent Table)和子表(Child Table)的插入、更新、删除操作均要占用系统的开销,另外,最好不要用Identify 属性字段作为主键与子表关联。如果数据冗余低,数据的完整性容易得到保证,但增加了表间连接查询的操作,为了提高系统的响应时间,合理的数据冗余也是必要的。使用规则(Rule)和约束(Check)来防止系统操作人员误输入造成数据的错误是设计人员的另一种常用手段,但是,不必要的规则和约束也会占用系统的不必要开销,需要注意的是,约束对数据的有效性验证要比规则快。所有这些,设计人员在设计阶段应根据系统操作的类型、频度加以均衡考虑。
事务的陷阱
---- 事务是在一次性完成的一组操作。虽然这些操作是单个的操作,SQL Server能够保证这组操作要么全部都完成,要么一点都不做。正是大型数据库的这一特性,使得数据的完整性得到了极大的保证。
---- 众所周知,SQL Server为每个独立的SQL语句都提供了隐含的事务控制,使得每个DML的数据操作得以完整提交或回滚,但是SQL Server还提供了显式事务控制语句 ---- BEGIN TRANSACTION 开始一个事务 ---- COMMIT TRANSACTION 提交一个事务 ---- ROLLBACK TRANSACTION 回滚一个事务 ---- 事务可以嵌套,可以通过全局变量@@trancount检索到连接的事务处理嵌套层次。需要加以特别注意并且极容易使编程人员犯错误的是,每个显示或隐含的事物开始都使得该变量加1,每个事务的提交使该变量减1,每个事务的回滚都会使得该变量置0,而只有当该变量为0时的事务提交(最后一个提交语句时),这时才把物理数据写入磁盘。 数据库性能调整
---- 在计算机硬件配置和网络设计确定的情况下,影响到应用系统性能的因素不外乎为数据库性能和客户端程序设计。而大多数数据库设计员采用两步法进行数据库设计:首先进行逻辑设计,而后进行物理设计。数据库逻辑设计去除了所有冗余数据,提高了数据吞吐速度,保证了数据的完整性,清楚地表达数据元素之间的关系。而对于多表之间的关联查询(尤其是大数据表)时,其性能将会降低,同时也提高了客 户端程序的编程难度,因此,物理设计需折衷考虑,根据业务规则,确定对关联表的数据量大小、数据项的访问频度,对此类数据表频繁的关联查询应适当提高数据冗余设计。
数据类型的选择
---- 数据类型的合理选择对于数据库的性能和操作具有很大的影响,有关这方面的书籍也有不少的阐述,这里主要介绍几点经验。
Identify字段不要作为表的主键与其它表关联,这将会影响到该表的数据迁移。 Text 和Image字段属指针型数据,主要用来存放二进制大型对象(BLOB)。这类数据的操作相比其它数据类型较慢,因此要避开使用。 日期型字段的优点是有众多的日期函数支持,因此,在日期的大小比较、加减操作上非常简单。但是,在按照日期作为条件的查询操作也要用函数,相比其它数据类型速度上就慢许多,因为用函数作为查询的条件时,服务器无法用先进的性能策略来优化查询而只能进行表扫描遍历每行。
---- 例如:要从DATA_TAB1中(其中有一个名为DATE的日期字段)查询1998年的所有记录。
---- Select * from DATA_TAB1 where datepart(yy,DATE)=1998 |
回复
|
|
| 12楼 |
2005-01-19 15:30:00 |
积分:0分
|
触发器的几种应用 李振华 李贵龙 刘涯 摘 要 列举了触发器的几种代表性应用:数据分散—集中式模型的设计,历史数据的导出,应用系统间的数据接口。并对如何设计这些触发器进行了探讨。
关键词 触发器,数据分散—集中模型,历史数据导出,数据接口 1 引言
在大型数据库设计中,会经常用到触发器。它的特点是:一旦被定义,就存在于后台数据库系统(server,服务器方)中,并会在相应条件下自动地隐式执行,从而使得它的设计既与前台(client,客户机方)的平台无关,又免除了前台相关的数据操作设计。
在文献[1]中,列举了触发器的几种应用:审计;复杂的完整性约束;复杂的安全性授权;事件登录;列值导出;分布式数据库中表复制。 2 触发器的另外几种应用
2.1 数据分散——集中式模型设计
在实际开发过程中,经常遇到这样的数据维护要求:单位由多个部门组成,要求各部门只能维护本部门的数据,但另一方面,又需要将分散到各部门的数据集中起来进行汇总,得到本单位的汇总数据。如一个学校有多个系,学校需要各系的成绩汇总;一个工厂有多个生产车间,工厂需要各车间的产量汇总;一个公司有多个销售部门,公司需要各部门的销量汇总等等。
在这种情况下,如果不使用触发器的话,数据库设计就存在困难:
. 如果为每个部门都建立一个表,显然难以得到汇总的数据(在这种情况下,无法利用视图机制);
. 如果所有的部门都共享一个表的话(这时,这张表中的数据实际就是汇总的数据),因为每个部门需要维护数据,所以都对这个表有修改权,因此在数据安全上难以控制。
使用触发器的话,上述问题便可迎刃而解:为每个部门建立一个表(该部门的所有权限只限于对此表有修改权),再为汇总数据也建立一个表,然后在每个部门表上建立触发器,使得部门表上有数据更新时,便会对应地更改汇总表中的相关数据(见图1)。 在这种模型中,要注意设计好部门表相关字段的完整性约束,使各部门表内的数据是唯一的,以防止不同部门表出现相同的数据记录,从而导致在汇总表中出现混乱。 2.2 历史数据导出
数据库中的表只记载最新的数据,而不记载历史数据。但在很多情况下,历史数据的记载与分析反而比现实数据更有意义(这也正是数据仓库与数据库的区别之一),比如学校中学号的变动,工厂定额的更改,公司产品和原材料价格的变化、股票的升跌等等,它们都需要记录历史数据。
如何使数据库也能记载历史数据呢?使用触发器可以解决这类问题。
建立这类触发器的步骤是:建立数据表后,再建立对应的历史表(一般而言,历史表在字段组成上是数据表的超集,即在原数据表字段上再增加有关时间的字段),然后在两者之间设立触发器。这样,每当数据表有数据变动,触发器便将变动的数据记入历史数据表中,从而达到自动记录历史数据的目的。
2.3 应用系统间的数据接口
一个完整的信息系统的建设一般不是一步到位的,往往是分期分批完成,而不同期次的系统往往又会有数据传递,然而由于需求发生变化或是其他原因,不同期次系统的数据库设计在表结构甚至字段上的设计都可能会互不一致(即使是在同一期的开发过程中,由于总体设计或数据字典方面的偏差或不足,或者需要集成多家系统,这种现象也会经常出现)。在不可能重建这些系统的情况下,它们之间的数据能无缝传递吗?换言之,它们之间能够做到无缝连接吗? 在这种情况下,触发器可以是一种较好的解决方式:建立中间表,中间表的设计符合需方应用系统的设计格式,而它的数据又与供方应用系统的数据保持一致。
要注意的一点是:图示应用系统间的数据是单向流动的(即数据传递);如果数据需要双向流动(即数据交换),那么在触发器设计中应有退出机制,以避免发生触发器的递归。 3 结语
触发器对数据库开发过程中遇到的问题,往往会有独到的解决方法。触发器能使数据库的设计变得简洁和高效。文中的3个例子,代表了触发器3个方面的典型应用。 作者简介:李振华 硕士,讲师。现从事计算机网络及数据库的教学与科研工作。 作者单位:中国地质大学网络中心 湖北.武汉(430074) 参考文献
[1] 沈佩娟,汤荷美,编著. 数据库管理及应用开发(第1版).北京:清华大学出版社,1995,6 ODBC中的同步与异步执行模式 -------------------------------------------------------------------------------- 1.引言
近年来,随着计算机局域网技术的不断发展,计算机体系结构已经发展到复杂而开放的客户机/服务器模式。对于客户机/服务器应用的开发,现在常用的前端开发工具有:VisualBasic、Delphi、PowerBuilder等。它们可通过ODBC接口访问服务器的SQLServer数据库服务器。
VisualBasic、Delphi、PowerBuilder等开发工具在使用ODBC2.0来编写程序时,通常会提供三种方法来进行数据库应用程序的方案设计:
·使用数据控制项
·使用数据库对象变量进行编程
·直接调用ODBC2.0API
在客户机/服务器模式下进行数据库应用程序设计时,仅用前两种方法往往是不够的。因为采用前两种方法,其执行模式对于程序员是透明的,而ODBC2.0访问数据库时存在同步与异步执行模式之分,故容易因设计不当,发生系统死锁。因此,在实际编程序时,我们需要采用第三种方法来解决由同步和异步执行模式所造成的问题。 2.同步和异步执行模式
ODBC2.0访问数据库时,有同步执行模式与异步执行模式之分。
所谓同步执行模式,是指语句在同步执行模式下,将始终保持对程序流的控制,直至程序结束。例如查询操作,客户机上的应用程序在向服务器发出查询操作的指令后,将一直等待服务器将查询结果返回客户机端后,才继续进行下一步操作,如图1所示。
图1同步执行模式
所谓异步执行模式,是指语句在异步执行模式下,各语句执行结束的顺序与语句执行开始的顺序并不一定相同。例如查询操作,客户机上的应用程序在向服务器发出了查询操作的指令后,将立刻执行查询语句的下一条语句,而不需要等到服务器将查询结果返回客户机端后,才继续进行下一步操作。如图2所示。
图2异步执行模式
在一些应用程序开发工具中,在其提供使用数据控制项和数据库对象变量进行编程的同时,并没有很好地考虑到同步执行模式与异步执行模式的重要区别。为了使程序运行速度更快,其语句执行的缺省模式为异步模式。对于一般程序员来说,如果他对同步执行模式与异步执行模式不了解的话,他往往会在对服务器发出一个操作语句(查询或读取一条记录等操作)后,立刻引用服务器返回的执行结果,或者对该结果进行下一步操作;在异步执行模式下,客户机上的后续语句是在该操作语句发出后接着执行的,但由于各种原因,服务器不一定能执行完该操作语句,并在后续语句执行前将结果返回客户机。因此,后续语句在引用前一操作语句的执行结果时,往往会因为该执行结果并不存在而引用了错误的值,造成系统错误或死锁。 3.解决方案
解决上面所提到的问题,可以采取以下两种方案:
①利用ODBC2.0API,将语句执行状态设置为同步执行模式。ODBC2.0API中,函数SQLSetStmtOption()的功能是设置同步或异步执行模式。我们可以采用以下语句,将语句执行状态设置为同步执行模式:iRetCodeΚSQLSetStmtOption(hStmt,SQL-ASYNC-ENABLE,0)
其中,hStmt是一有效的语句句柄,常数SQL-ASYNC-ENABLE是所要设置的选项,参数0表示该选项(即异步执行模式)关闭。如果iRetCode返回SQL-SUCCESS,则表示语句执行状态已被设置为同步执行模式。
②利用ODBC2.0API,将语句执行状态设置为异步执行模式,然后在程序中不断查询一个操作语句是否已经执行完毕。
ODBC2.0API中共有20多个函数支持异步执行,如上页表所示。
这些函数第一次调用后,将返回值SQL-STILL-EXECUTING,这时应用程序将继续执行后续语句。过了一段时间后,应该再次调用原函数,而且要注意:实参数应传入与第一次调用时相同的语句句柄,其他参数也应一样(但会被忽略)。如果函数返回值为SQL-SUCCESS,则表明该语句已经执行完毕;如果函数返回SQL-STILL-EXECUTING,则表明该语句仍在执行中。
我们可以用一个简单的例子说明如下:
iRetCodeΚSQLSetStmtOption(hStmt,SQL-ASYNC-ENABLE,1)
′置语句执行模式为异步执行模式
iRetCodeΚSQLExecDirect(hStmt,″SELECT*FROMemployees″,23)
......′执行其他操作
iRetCodeΚSQLExecDirect(hStmt,″SELECT*FROMemployees″,23)
′判断SQLExecDirect()是否已执行完毕
If(iRetCodeΚSQL-STILL-EXECUTING)Then
......′该语句未执行完,继续执行其他操作
Else
If(iRetCodeΚSQL-SUCCESS)Then
......′该语句已执行完,可对语句操作结果进行处理
EndIf
EndIf
同步执行模式可以简化程序编制的复杂性,对ODBC2.0API不十分熟悉的程序员,可以不用过多地了解比较复杂的ODBC2.0API,而只需使用数据控制项和数据库对象变量来编写应用程序,使开发效率大大提高,但程序运行速度比不上异步执行模式的速度。
异步执行模式虽然在编程序时十分复杂,但在这种模式下可以进行多任务并行执行,使执行效率大大提高。
我们在编制应用程序时,应根据自身的情况,对这两种模式的使用进行划分,以便既提高程序运行的安全可靠性,又提高程序执行的效率。 |
回复
|
|
| 13楼 |
2005-01-19 15:31:00 |
积分:0分
|
提高客户机/服务器应用系统性能的一些方法 作者:陈霞 罗铁坚
摘 要 客户机/服务器的应用逻辑设计是否合理,直接影响到客户机/服务器方式应用系统的处理性能。本文以Oracle 7服务器为平台提出一些方法,以确定如何划分应用逻辑,使客户机/服务器应用系统有较高的处理效率。
关键词 PL/SQL 触发子 客户机/服务器 一、问题的提出
80年代末到90年代初,许多应用系统从主机终端方式、文件共享方式向客户机/服务器方式过渡。客户机/服务器系统比文件服务器系统能提供更高的性能,因为客户机和服务器将应用的处理要求分开,同时又共同实现其处理要求(即"分布式应用处理")。服务器为多个客户机管理数据库,而客户机发送请求和分析从服务器接收的数据。在一个客户机/服务器应用中,数据库服务器是智能化的,它只封锁和返回一个客户机请求的那些行,保证了并发性,使网络上的信息传输减到最少,因而可以改善系统的性能。在客户机/服务器系统中,应用的处理是分布在网络上的,所以在设计客户机方数据库应用程序时,若把过多的负担加在网络上,没有充分利用数据库的存储过程,没有把网络访问最小化,结果是应用程序要执行过多的网络I/O,使网络饱和,从而降低了整个系统的性能。要开发好的客户机/服务器应用系统,必须搞清楚如何在组成系统的各部分之间分布应用功能。
本文提出一些方法来确定客户机/服务器数据库系统中哪些功能应放在哪里实现。客户机应用程序主要侧重于用某种方便用户的方式表示和(或)分析数据。开发客户机应用程序时,网络传输量是应重点考虑的问题之一。应注意应用程序如何向数据库服务器发送信息或从数据库服务器接收信息以及发送和接收多少数据。通常客户机/服务器系统上网络I/O是应用程序性能的瓶颈,一个应用程序引起的网络上I/O越少,应用及整个系统的运行情况越好。要从一个客户机应用中消除不必要的网络传输量,需要理解和利用SQL命令及数据库上的其它一些特征。 二、一个例子
下面,我们来考虑这样一个例子:一个数据库应用程序完成显示每个销售订单的所有行的金额合计。
一般算法分两步:
(1)每行项目中数量乘以单价=金额;
(2)把每行金额累加。
第一种方法:
每次一行,然后,用应用程序累加,即
SELECT orderid, quantity ,unitprice
FROM item ,stock
WHERE stock.id=item.id
ORDER BY orderid
结果为:
orderid quantity unitprice
1 1 6.03
1 1 21.4
1 4 87.12
2 2 8.97
2 3 21.4
采用这种方法,若业务量增大,则其网络的传输量相应增大。
第二种方法:
让数据库服务器进行计算,然后只把结果从网上取过来,即:
SELECT orderid ,SUM(quantity *unitprice)
FROM item , stock
WHERE item.id=stock.id
GROUP BY orderid
ORDER BY orderid
结果为:
orderid SUM(quantity*unitprice)
1 265.87
2 82.14
如上所示,由于第二种查询使用了一个SQL的组合,即一个SQL函数(SUM)以及一个GROUP BY子句,让服务器来执行计算。因此,只需从网上传输较少的数据。
这个简单例子说明: 1.在客户机/服务器环境中如何用SQL函数减少网络上的传输量;
2.开发人员必须完全熟悉SQL才能做出好的客户机数据库应用程序。
三、提高性能的方法
下面我们介绍几种提高性能的方法。 1.使用完整性约束
所有客户机应用程序必须遵循一系列预先定义的数据完整性规划及业务规则,以保证所有数据库数据是合法的。可以使用两种方法来实施一个简单的完整的规则:让应用程序执行完整性检查;使用Oracle7的完整性约束。
(1)让应用程序执行完整性检查
例如,任何一个订单( orders)的顾客号必须是客户机表( customer)中的顾客户机号。这是一个最简单的引用完整性,可以用应用程序本身执行完整性检查。
DECLARE
flag INTEGER;
BEGIN
SELECT id INTO flag
FROM customer
WHERE id=3
FOR UPDATE OF id;
IF SQL%FOUND THEN
INSERT INTO orders
VALUES(5,3,SYSDATE,null,null,'F');
-- 其他应用逻辑
COMMIT;
END IF;
END;
这个过程只是在应用程序内部用于实施引用完整性规则的一种方法,但可以看出,为实施一个简单的完整性规则,应用程序要花大量的时间通过网络来请求和发送数据。(2)使用Oracle 7的完整性约束
CREATE TABLE orders
(
id INTEGER PRIMARY KEY,
customer id INTEGER NOT NULL REFERENCES customer,
orderdate DATE NOT NULL,
shipdate DATE DEFAULT SYSDATE,
paidate DATE DEFAULT SYSDATE,
status CHAR(1) DEFAULT ‘F' CHECK(status IN(‘F'
,‘B'))
);
实施简单完整性规则(如引用完整性)的更好方法是使用Oracle 7的完整性约束。该方法的好处是明显的:
①定义一个表的同时,方便地建立完整性约束,开发人员不需为实施一个简单的完整性规则而创建测试、排错复杂的数据完整性逻辑,提高了工作效率;
②用集中化的方法实现完整性规则;
③无需任何网络I/O,客户机/服务器系统也就不会因为网络访问而降低性能。
2.使用数据库触发器
应用程序经常需要实施复杂的业务规则,这些规则无法用完整性规则表示,所以最好不要按照常规思路在应用程序中实施完整性规则;而是用数据触发子(triggers)来实施业务规则。其优点是容易创建,可集中进行规则实施,避免不必要的网络I/O。利用数据库触发子可以使其他一些应用程序集中化和自动化。 比如,计算item 表中的total 列的值是所订零件的数量乘以零件的单价,而零件单价存放在stock表中,当插入一个新的行项目时,应用程序计算total列的值有两种方法。
方法一:让应用程序通过SQL命令执行这个操作
DECLARE total REAL;
BEGIN
SELECT unitprice*quantity INTO total
FROM stock, item
WHERE id=4;
INSERT INTO item VALUES(...);
END
应用程序通过网络发出请求,取得某一些零件单价,然后插入这个含有该行计算值(tota l)的行。修改item表中某行数量值,应用程序需要包含相似的逻辑来计算。此外,多个用户还可能同一时刻插入和修改订单。总之,用这种方法来计算total 列时会在客户机/服务器系统中产生大量的网络传输。
方法二:用数据库触发子, 从一个行项目自动导出total 的值,
当用户在item表中插入新行或修改quantity时,无需任何网络访问。
CREATE TRIGGER Linetotal
BEFORE INSERT OR UPDATE OF quantity,stockid
ON item
FOR EACH ROW
DELARE
itemprice REAL;
BEGIN
SELECT unitprice
INTO itemprice
FROM stock
WHERE id=:new.stockid;
:new.tolal:=new.quantity*itemprice;
END linetotal;
当创建触发器linetotal后, 应用开发人员在编写应用程序时就不需考虑保持total列为最新值的问题,而且网络数据库上所有应用都会因此受益。
3.利用过程和包优化性能
这里主要讨论如何利用完整性约束和数据库触发器把应用逻辑移到数据库服务器中执行以便减少网络I/O,提高性能。
其它类型应用处理逻辑分布到数据库服务器亦可以减少客户机/服务器应用中的网络I/ O,应用程序不必再用包含多个网络操作的SQL语句去执行数据库服务器操作,而是简单且有效地调用存储过程。包是一种用来把多个有关的过程在数据库中封装起来的方法。
下面是用SQL和用存储过程例子的差异:
例如要插入某些行项目的订单,用SQL实现过程:
INSERT INTO orders VALUES(...)
INSERT INTO item VALUES(1,..)
UPDATE stock SET onhand=...
INSERT INTO orders VALUES(...)
INSERT INTO item VALUES(2,..)
UPDATE stock SET onhand=...
INSERT INTO orders VALUES(...)
INSERT INTO item VALUES(3,..)
UPDATE stock SET onhand=...
COMMIT;
要创建一个新销售订单并插入它的三个行项目,应用程序必须用7个不同的的SQL语句来实现,每个语句都要通过网络传输数据,要减少这些SQL语句在客户机/服务器系统中产生的网络传输量,可以创建两个简单的过程来插入订单及行项目。
CREATE PROCEDURE placeorder(custid IN INTEGER) AS
BEGIN
INSERT INTO orders
VALUES(orderseq.NEXTVAL,custid,SYSDATE,null,null,‘F
');
END placeorder;
CREATE PROCEDURE placeitem
(itemid IN INTEGER,partid IN INTEGER, quan IN INTEGER)
AS
BEGIN
INSERT INTO item(id,orderid,stockid,quantity)
VALUES(itemid,orderseq.CURRVAL,partid,quan);
UPDATE stock
SET onhand=onhand-quan
WHERE id=partid;
END placeitem;
应用程序只需简单调用这几个过程。
Placeorder(3);
Placeitem(1,3,2);
Placeitem(2,8,1);
Placeitem(3,9,3);
当一个应用程序调用存储过程时,通过网络发送的数据只有过程调用及参数。本文提出的提高客户机/服务器应用系统性能的方法,主要是采用合理分布处理逻辑于客户机端与服务器端,并充分利用数据库服务器来提高执行速度。该方法的有效性,已在我们开发的多项客户机/服务器应用系统中得到证实。此外,在提高性能的措施和方法中,SQL语句的查询优化问题也不可忽视。综合上述两种方法,才能从根本上提高
客户机/服务器应用系统的性能。 参考文献
1 Steven M.Bobrowski. Mastering Oracle7 & Client/Server
Computing, SYBEX I nc. 1995.
2 Michael J. Corey etc. Oracle数据库性能优化技术,学苑
出版社,1994.
|
回复
|
|
| 14楼 |
2005-01-19 15:31:00 |
积分:0分
|
优化SQL执行效能的几点常识 我想大家都知道,优化数据库的执行效能是一个专门的技术,Oracle有这方面专门的图书,如Oracle Tunning Tec.本文只涉及了很少的一部分--优化SQL的一些小常识。 1)不知大家是否喜欢使用‘NOT IN’这样的操作,如果是,那尽量使用(NOT) EXISTS 替代 例子:
语句1 SELECT dname, deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp); 语句2 SELECT dname, deptno FROM dept WHERE NOT EXISTS (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno); 明显的,2要比1的执行性能好很多 因为1中对emp进行了full table scan,这是很浪费时间的操作。而且1中没有用到emp的index, 因为没有where子句。而2中的语句对emp进行的是range scan。 2)在海量查询时尽量少用格式转换。 如用 WHERE a.order_no = b.order_no 而不用 WHERE TO_NUMBER (substr(a.order_no, instr(b.order_no, '.') - 1) = TO_NUMBER (substr(a.order_no, instr(b.order_no, '.') - 1)
3)查询海量数据是,可以使用optimizer hints,例如/*+ORDERED */ 如 SELECT /*+ FULL(EMP) */ E.ENAME FROM EMP E WHERE E.JOB = 'CLERK'; 而不是 SELECT E.ENAME FROM EMP E WHERE E.JOB || '' = 'CLERK'; |
回复
|
|
| 15楼 |
2005-01-19 15:31:00 |
积分:0分
|
客户/服务器远程数据传输处理技巧 ---- 在 实 际 的MIS 系 统 中, 远 程 数 据 库 访 问 大 多 通 过Modem 连 接, 出 于 通 信 费 用 及 速 度 方 面 的 考 虑, 往 往 采 用 先 将 数 据 保 存 在 本 地, 然 后 集 中 传 送 到 远 端 的 办 法。 远 程 数 据 传 送 可 以 有 多 种 方 案, 最 常 见 的 是 先 将 要 传 送 的 数 据 打 包 成 文 件, 在 利 用 文 件 传 输 形 式 传 送 到 目 的 地, 在 目 的 地 对 数 据 恢 复 后 添 加 到 本 地 数 据 库 中。 这 种 方 法 普 遍 地 应 用 于 证 券 交 易 系 统, 其 优 点 是 速 度 快, 并 且 可 事 先 对 数 据 压 缩, 更 大 限 度 地 节 约 传 送 时 间 及 费 用。 但 这 种 方 案 也 有 其 不 足 之 处: 由 于 利 用 文 件 传 输 机 制, 无 法 利 用 数 据 库 本 身 的 特 性 如 完 整 性 约 束、 数 据 一 致 性、 回 滚 机 制 等, 因 此 在 比 较 复 杂 的 数 据 库 系 统 中 较 少 采 用。 另 一 种 方 法 是 直 接 将 两 端 处 理 成" 客 户/ 服 务 器" 模 式, 将 数 据 传 送 看 成 是 向Server 提 交 数 据。 由 于 这 种 方 案 充 分 利 用 了 数 据 库 服 务 器 的 特 性, 并 且 实 际 操 作 基 本 与 局 域 网 方 式 一 致, 因 此 本 文 将 详 细 介 绍 这 种 方 案。 另 外 本 文 的 部 分 内 容 是 基 于Delphi/CBuilder 的。 ---- 由 于 传 输 速 度 的 原 因, 当 传 送 大 量 数 据 时 绝 对 不 赞 成 逐 条 记 录 地 向 服 务 器 提 交 数 据, 而 应 批 量 地 向Server 提 交,Delphi/CBuilder 中 提 供 了 一 个TBatchMove 控 件 专 门 用 于 批 量 传 送 数 据, 利 用 它 可 极 大 减 少 网 络 负 担, 提 高 传 送 速 度。 遗 憾 的 是,TBatchMove 控 件 只 提 供 了 简 单 的 错 误 控 制 功 能, 没 有 提 供 显 示 传 送 进 度、 用 户 终 止 传 送 等 重 要 功 能。 然 而TBatchMove 所 依 赖 的BDE 却 提 供 了 一 种" 回 调 机 制" 可 以 完 成 上 述 两 个 功 能。 所 谓" 回 调" 过 程 是 这 样 的: 当BDE 执 行 某 种 操 作 时, 比 如 从 一 张 表 向 另 一 张 表 拷 贝 大 量 数 据 的 过 程 中, 每 过 一 段 时 间( 如 需 要 显 示 拷 贝 进 度 时),BDE 会 调 用 一 段 你 自 己 写 的 函 数( 回 调 函 数), 以 帮 助 你 更 完 全 地 控 制 程 序。 这 种 做 法 有 点 想DLPHI 中 的Event( 事 件) 及 事 件 处 理 函 数-- 某 个 具 体 的 操 作 动 作 会 让VCL 触 发 某 个 事 件, 从 而 调 用 一 段 你 写 好 的 事 件 处 理 函 数, 不 同 的 事 件 会 触 发 不 同 的 处 理 函 数。 ---- 为 了 让BDE 能 正 确 地 与 你 的 函 数 协 同 工 作, 你 必 须 事 先" 注 册" 你 的 函 数, 让BDE 知 道 某 个 事 件 发 生 时 应 调 用( 回 调) 你 的 某 段 代 码。BDE 提 供 了 一 个DbiRegisterCallBack 注 册 函 数, 不 幸 的 是,BDE 的 联 机 帮 助 中 的 说 明 不 能 适 合 于Delphi/CBuilder, 按 照 该 说 明 编 写 的 程 序 根 本 不 能 通 过 编 译 ! 笔 者 通 过 实 践 找 到 了 正 确 使 用BDE 回 调 函 数 的 方 法, 下 面 将 详 细 介 绍 该 机 制 的 使 用。 BDE 回 调 机 制 包 含 以 下 几 个 步 骤: ---- 1) 按BDE 的 预 定 格 式 编 写 你 的 回 调 函 数 ---- 2) 调 用DbiRegisterCallBack 函 数 注 册 你 的 回 调 函 数, 这 样 当 你 执 行 相 关 数 据 库 操 作 时 就 自 然 地 触 发 你 的 回 调 函 数。 ---- 3) 执 行 相 关 数 据 库 操 作, 比 如BatchMove1- >Exectue(); ---- 4) 注 销 该 回 调 函 数 ---- 其 中 最 关 键 的 是 正 确 注 册 你 的 回 调 函 数, 因 此 先 介 绍 第 二 步。( 注 册 与 注 销 都 调 用 同 一 函 数, 只 是 最 后 一 个 参 数 略 有 不 同) ---- 首 先 你 应 知 道 在 哪 类" 事 件" 发 生 时 调 用 你 的 回 调 函 数, 其 次 你 应 明 白 与 该 事 件 相 关 的 参 数 及 数 据 结 构-- 这 一 切 都 发 生 在 调 用DbiRegisterCallBack 函 数 注 册 时, 所 以 下 面 先 介 绍DbiRegisterCallBack 的 正 确 用 法 及 说 明: ---- 在 原BDE 帮 助 中 该 函 数 的 原 形(C) 是 这 样 的 DBIResult DBIFN DbiRegisterCallBack (hCursor,
ecbType, iClientData, iCbBufLen, pCbBuf, pfCb); ---- 要 使 用 该 函 数 必 须include 头 文 件, 问 题 是Delphi/CBuilder 中 根 本 没 有 提 供 该 文 件, 取 而 代 之 的 是"BDE.HPP", 但 是 在 包 含 进 该 文 件 后 程 序 仍 然 不 能 编 译 通 过, 因 为 该 文 件 中 没 有DBIFN 等 的 说 明。 一 个 简 单 的 方 法 是 在 代 码 中 去 掉DBIFN。 函 数 中 各 参 数 解 释 如 下:hCursor 是 一 个BDE 中 对 象 的 句 柄, 如 果 这 个 参 数 为NULL, 则 表 示 注 册 的 回 调 函 数 适 合 于 所 有BDE 任 务; 第 二 个 参 数ecbType 是 指 回 调 函 数 的 触 发 条 件 的 类 别, 有 很 多 种 类 型 可 以 选 择, 其 中cbGENPROGRESS 表 示 当 需 要 显 示 一 个 长 操 作 的 进 度 时 触 发 这 个 回 调 函 数; 第 三 个 参 数iClientData 是 传 递 给 回 调 函 数 的 某 个 数 据 结 构 的 指 针, 在 我 们 的 例 子 中 为NULL; 第 四 个 参 数iCbBufLen 是 指 回 调Buffer 的 大 小, 该 大 小 随 第 二 个 参 数 的 不 同 而 不 同, 比 如sizeof(CBPROGRESSDesc); 第 五 个 参 数pCbBuf 是 回 调Buffer 的 指 针, 该 指 针 类 型 随 第 二 个 参 数 变 化, 比 如cbGENPROGRESS 的 数 据 结 构 是CBPROGRESSDesc; 最 后 一 个 参 数 是 回 调 函 数 的 地 址 指 针, 当 该 参 数 为NULL 时 表 示 注 销 该 类 型 的 回 调 函 数。 关 于 回 调 函 数 将 在 稍 后 详 细 介 绍。 下 面 是 注 册 执 行 长 操 作 时 显 示 进 度 的 回 调 函 数 的 格 式: int rst= DbiRegisterCallBack (NULL,
//适合于任何进程
cbGENPROGRESS, //回调类型:显示长操作的进度
NULL, //没有数据
sizeof(CBPROGRESSDesc), //数据结构的大小
&aCBBuf, //数据的内存地址
ApiCallBackFun //回调函数的地址
); ---- 接 下 来 就 应 该 完 成 第 一 步: 编 写 回 调 函 数 ---- 在C 中, 回 调 函 数 应 如 下 声 明: CBRType __stdcall ApiCallBackFun(
CBType ecbType, //回调类型
int iClientData, //回调数据(指针)
void * pCbInfo //回调数据结构指针
) ---- 第 一 个 参 数 是 回 调 类 型; 第 二 个 参 数 是 回 调 数 据, 其 解 释 同DbiRegisterCallBack 的 第 三 个 参 数; 第 三 个 是 回 调 数 据 的 指 针, 该 数 据 的 结 构 随 回 调 类 型 的 不 同 而 不 同。 比 如 进 度 指 示cbGENPROGRESS 的 数 据 结 构 是CBPROGRESSDesc, 其 定 义 如 下: struct CBPROGRESSDesc {
short iPercentDone; //进度的百分比
char szMsg[128]; //进度的文本信息
}; ---- 该 结 构 的 两 个 域 同 时 只 有 一 个 起 作 用, 第 一 个 表 示 操 作 的 进 度 百 分 比, 当 其 为-1 时 表 示 第 二 个 域 起 作 用。 第 二 个 域 用 字 符 串 表 示 进 度 信 息, 其 格 式 为< String >< : >< Value >, 比 如:Records Copied : 125 ---- 本 文 主 要 在 回 调 函 数 中 完 成 两 个 工 作: ---- 1) 显 示 数 据 拷 贝(BatchMove) 进 度 ---- 2) 提 供 让 用 户 终 止 长 时 间 拷 贝 的 机 制 ---- 显 示 拷 贝 进 度 的 代 码 如 下: CBRType __stdcall ApiCallBackFun(
CBType ecbType, // Callback type
int iClientData, // Client callback data
void * pCbInfo // Call back info/Client)
{ AnsiString str;
if(ecbType==cbGENPROGRESS)
{
int j= StrToInt( ((CBPROGRESSDesc*)
pCbInfo)- >iPercentDone);
if(j< 0)
//如果iPercentDone为-1,则分析szMsg的信息
{
str=((CBPROGRESSDesc*)pCbInfo)- >szMsg;
int pos=str.AnsiPos(":")+1;
//提取出拷贝的记录数
//下面的代码用来在一个Form中显示拷贝进度及拷贝数量
Form1- >Label2- >Caption= str.SubString(pos,100);
Form1- >Label2- >Update();
Form1- >ProgressBar1- >Position=
int((str.SubString(pos,100).
ToDouble()/Form1- >TransNum)*100);
Form1- >ProgressBar1- >Update();
}
else
{Form1- >ProgressBar1- >Position=j;
Form1- >ProgressBar1- >Update();
}
return cbrCONTINUE;
//必须返回cbrCONTINUE以便让BatchMove继续
//若返回cbrABORT则终止拷贝
} ---- 一 切 完 成 以 后, 每 当 调 用 长 时 间BDE 操 作( 比 如BatchMove1->Exectue()) 时 都 会 触 发 该 回 调 函 数, 注 意 在 不 需 要 时 应" 注 销" 这 个 回 调 函 数。 ---- 如 果 批 量 传 送 数 据 时 间 很 长, 则 必 须 为 用 户 提 供 终 止 该 操 作 的 机 会, 前 面 提 到, 若 回 调 函 数 返 回cbrABORT, 则BatchMove 过 程 立 即 终 止。 可 以 在Form 上 加 上 一 个" 停 止" 按 钮 和 一 个 全 局 布 尔 变 量isContinue, 当 开 始 拷 贝 时 设 该 变 量 为true, 当 按 钮 按 下 后, 设 该 变 量 为false, 每 次 调 用 回 调 函 数 时 检 查isContinue 的 值, 若 为true 则 回 调 函 数 返 回cbrCONTINUE 让 拷 贝 继 续, 否 则 返 回cbrABORT 终 止 拷 贝。 但 是 问 题 在 于 一 旦 拷 贝 过 程 开 始, 该 进 程 内 所 有 消 息 将 被 阻 塞, 应 用 程 序 在 拷 贝 结 束 之 前 没 有 机 会 响 应 键 盘、 鼠 标 等 一 切 消 息, 连 屏 幕 刷 新 都 不 能 完 成, 因 此 必 须 找 到 一 种 避 免 消 息 阻 塞 的 方 法。 ---- 大 家 知 道,Windows 是 靠 事 件( 消 息) 驱 动 的, 在WIN32 系 统 中 有 两 种 消 息 队 列: 系 统 队 列 和 应 用 程 序 队 列, 当 一 个 程 序 进 行 一 个 长 时 间 操 作 时, 系 统 分 配 给 该 程 序 的 时 间 片 将 完 全 用 于 处 理 该 操 作, 换 句 话 说, 应 用 程 序 没 有 从 它 的 应 用 程 序 队 列 中 取 出 消 息 并 处 理 的 机 会, 这 样 该 程 序 将 停 止 一 切 对 外 部 事 件 的 响 应 直 到 该 操 作 完 成 为 止。 具 体 到 本 文 中 就 是 程 序 必 须 等 到BatchMove1- >Execute() 执 行 完 毕 后 才 能 响 应 用 户 操 作, 因 此 用 户 将 完 全 没 有 机 会 终 止 拷 贝 过 程。 ---- 解 决 的 办 法 是: 在 回 调 函 数 中 取 出 消 息 队 列 中 的 消 息, 并 后 台 处 理 它 们, 这 样 用 户 将 有 机 会 按 下 终 止 按 钮。 实 现 的 代 码 很 简 单, 在 回 调 函 数 中 最 后 加 入 以 下 代 码 即 可 CBRType __stdcall ApiCallBackFun(…)
{
……
MSG amsg;
while(PeekMessage(&amsg,NULL,0,0,PM_REMOVE))
//从队列中取消息
{
TranslateMessage(&amsg); //翻译消息
DispatchMessage(&amsg); //分发消息
}
if (isContinue)
return cbrCONTINUE;
else
return cbrABORT;
} ---- 以 上 的 代 码 虽 然 都 用CBuilder 编 写, 但 是 其 原 理 同 样 适 用 于DELPHI。 |
回复
|
|
| 16楼 |
2005-01-19 15:32:00 |
积分:0分
|
SQL查询语句使用
作者:任我行
文章属性:转贴
一、简单查询
简单的Transact-SQL查询只包括选择列表、FROM子句和WHERE子句。它们分别说明所查询列、查询的
表或视图、以及搜索条件等。
例如,下面的语句查询testtable表中姓名为“张三”的nickname字段和email字段。
SELECT nickname,email
FROM testtable
WHERE name='张三' (一)选择列表
选择列表(select_list)指出所查询列,它可以是一组列名列表、星号、表达式、变量(包括局部变
量和全局变量)等构成。
1、选择所有列
例如,下面语句显示testtable表中所有列的数据:
SELECT *
FROM testtable 2、选择部分列并指定它们的显示次序
查询结果集合中数据的排列顺序与选择列表中所指定的列名排列顺序相同。
例如:
SELECT nickname,email
FROM testtable 3、更改列标题
在选择列表中,可重新指定列标题。定义格式为:
列标题=列名
列名 列标题
如果指定的列标题不是标准的标识符格式时,应使用引号定界符,例如,下列语句使用汉字显示列
标题:
SELECT 昵称=nickname,电子邮件=email
FROM testtable 4、删除重复行
SELECT语句中使用ALL或DISTINCT选项来显示表中符合条件的所有行或删除其中重复的数据行,默认
为ALL。使用DISTINCT选项时,对于所有重复的数据行在SELECT返回的结果集合中只保留一行。 5、限制返回的行数
使用TOP n [PERCENT]选项限制返回的数据行数,TOP n说明返回n行,而TOP n PERCENT时,说明n是
表示一百分数,指定返回的行数等于总行数的百分之几。
例如:
SELECT TOP 2 *
FROM testtable SELECT TOP 20 PERCENT *
FROM testtable (二)FROM子句
FROM子句指定SELECT语句查询及与查询相关的表或视图。在FROM子句中最多可指定256个表或视图,
它们之间用逗号分隔。
在FROM子句同时指定多个表或视图时,如果选择列表中存在同名列,这时应使用对象名限定这些列
所属的表或视图。例如在usertable和citytable表中同时存在cityid列,在查询两个表中的cityid时应
使用下面语句格式加以限定:
SELECT username,citytable.cityid
FROM usertable,citytable
WHERE usertable.cityid=citytable.cityid
在FROM子句中可用以下两种格式为表或视图指定别名:
表名 as 别名
表名 别名 例如上面语句可用表的别名格式表示为:
SELECT username,b.cityid
FROM usertable a,citytable b
WHERE a.cityid=b.cityid SELECT不仅能从表或视图中检索数据,它还能够从其它查询语句所返回的结果集合中查询数据。
例如:
SELECT a.au_fname+a.au_lname
FROM authors a,titleauthor ta
(SELECT title_id,title
FROM titles
WHERE ytd_sales>10000
) AS t
WHERE a.au_id=ta.au_id
AND ta.title_id=t.title_id
此例中,将SELECT返回的结果集合给予一别名t,然后再从中检索数据。 (三)使用WHERE子句设置查询条件
WHERE子句设置查询条件,过滤掉不需要的数据行。例如下面语句查询年龄大于20的数据:
SELECT *
FROM usertable
WHERE age>20 WHERE子句可包括各种条件运算符:
比较运算符(大小比较):>、>=、=、<、<=、<>、!>、!<
范围运算符(表达式值是否在指定的范围):BETWEEN…AND…
NOT BETWEEN…AND…
列表运算符(判断表达式是否为列表中的指定项):IN (项1,项2……)
NOT IN (项1,项2……)
模式匹配符(判断值是否与指定的字符通配格式相符):LIKE、NOT LIKE
空值判断符(判断表达式是否为空):IS NULL、NOT IS NULL
逻辑运算符(用于多条件的逻辑连接):NOT、AND、OR 1、范围运算符例:age BETWEEN 10 AND 30相当于age>=10 AND age<=30
2、列表运算符例:country IN ('Germany','China')
3、模式匹配符例:常用于模糊查找,它判断列值是否与指定的字符串格式相匹配。可用于char、
varchar、text、ntext、datetime和smalldatetime等类型查询。
可使用以下通配字符:
百分号%:可匹配任意类型和长度的字符,如果是中文,请使用两个百分号即%%。
下划线_:匹配单个任意字符,它常用来限制表达式的字符长度。
方括号[]:指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。
[^]:其取值也[] 相同,但它要求所匹配对象为指定字符以外的任一个字符。 例如:
限制以Publishing结尾,使用LIKE '%Publishing'
限制以A开头:LIKE '[A]%'
限制以A开头外:LIKE '[^A]%' 4、空值判断符例WHERE age IS NULL 5、逻辑运算符:优先级为NOT、AND、OR (四)查询结果排序
使用ORDER BY子句对查询返回的结果按一列或多列排序。ORDER BY子句的语法格式为:
ORDER BY {column_name [ASC|DESC]} [,…n]
其中ASC表示升序,为默认值,DESC为降序。ORDER BY不能按ntext、text和image数据类型进行排
序。
例如:
SELECT *
FROM usertable
ORDER BY age desc,userid ASC
另外,可以根据表达式进行排序。
二、联合查询
UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合合并成一个结果集合显示,即执行联
合查询。UNION的语法格式为:
select_statement
UNION [ALL] selectstatement
[UNION [ALL] selectstatement][…n]
其中selectstatement为待联合的SELECT查询语句。
ALL选项表示将所有行合并到结果集合中。不指定该项时,被联合查询结果集合中的重复行将只保留一
行。
联合查询时,查询结果的列标题为第一个查询语句的列标题。因此,要定义列标题必须在第一个查询语
句中定义。要对联合查询结果排序时,也必须使用第一查询语句中的列名、列标题或者列序号。
在使用UNION 运算符时,应保证每个联合查询语句的选择列表中有相同数量的表达式,并且每个查询选
择表达式应具有相同的数据类型,或是可以自动将它们转换为相同的数据类型。在自动转换时,对于数值类
型,系统将低精度的数据类型转换为高精度的数据类型。
在包括多个查询的UNION语句中,其执行顺序是自左至右,使用括号可以改变这一执行顺序。例如:
查询1 UNION (查询2 UNION 查询3) 三、连接查询
通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点,也是它区别于其它类型
数据库管理系统的一个标志。
在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在
一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带
来很大的灵活性,他们可以在任何时候增加新的数据类型。为不同实体创建新的表,尔后通过连接进行
查询。
连接可以在SELECT 语句的FROM子句或WHERE子句中建立,似是而非在FROM子句中指出连接时有助于
将连接操作与WHERE子句中的搜索条件区分开来。所以,在Transact-SQL中推荐使用这种方法。
SQL-92标准所定义的FROM子句的连接语法格式为:
FROM join_table join_type join_table
[ON (join_condition)]
其中join_table指出参与连接操作的表名,连接可以对同一个表操作,也可以对多表操作,对同一
个表操作的连接又称做自连接。
join_type 指出连接类型,可分为三种:内连接、外连接和交叉连接。内连接(INNER JOIN)使用比
较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。根据所使用
的比较方式不同,内连接又分为等值连接、自然连接和不等连接三种。
外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)
和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列出与连接条件相匹
配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的
数据行。
交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的
数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
连接操作中的ON (join_condition) 子句指出连接条件,它由被连接表中的列和比较运算符、逻辑
运算符等构成。
无论哪种连接都不能对text、ntext和image数据类型列进行直接连接,但可以对这三种列进行间接
连接。例如:
SELECT p1.pub_id,p2.pub_id,p1.pr_info
FROM pub_info AS p1 INNER JOIN pub_info AS p2
ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info) (一)内连接
内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值。内连接分
三种:
1、等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接
表中的所有列,包括其中的重复列。
2、不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些
运算符包括>、>=、<=、<、!>、!<和<>。
3、自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询
结果集合中所包括的列,并删除连接表中的重复列。
例,下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社:
SELECT *
FROM authors AS a INNER JOIN publishers AS p
ON a.city=p.city 又如使用自然连接,在选择列表中删除authors 和publishers 表中重复列(city和state):
SELECT a.*,p.pub_id,p.pub_name,p.country
FROM authors AS a INNER JOIN publishers AS p
ON a.city=p.city (二)外连接
内连接时,返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件
的行。而采用外连接时,它返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外
连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。
如下面使用左外连接将论坛内容和作者信息连接起来:
SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b
ON a.username=b.username 下面使用全外连接将city表中的所有作者以及user表中的所有作者,以及他们所在的城市:
SELECT a.*,b.*
FROM city as a FULL OUTER JOIN user as b
ON a.username=b.username (三)交叉连接
交叉连接不带WHERE 子句,它返回被连接的两个表所有数据行的笛卡尔积,返回到结果集合中的数
据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
例,titles表中有6类图书,而publishers表中有8家出版社,则下列交叉连接检索到的记录数将等
于6*8=48行。
SELECT type,pub_name
FROM titles CROSS JOIN publishers
ORDER BY type |
回复
|
|
| 17楼 |
2005-01-19 15:33:00 |
积分:0分
|
数据库应用中并发控制若干实现途径 张在建 一.引言
并发控制是指在多用户的环境下,对数据库进行并发操作进行规范的机制。其目的是为了避免对数据的丢失修改、读脏数据与不可重复读等,从而保证数据的正确性与一致性。并发控制在多用户的模式下是十分重要的,但这一点经常被一些数据库开发人员忽视,而且因为并发控制的层次和类型非常丰富,有时使人在选择时比较迷惑,不清楚衡量并发控制层次选择的原则和途径。本文将从一个例子入手,结合数据库理论的相关知识,对数据库应用中并发控制的途径、方法做出一个较全面的总结,希望能帮助读者找到合理的并发控制方法。
二.一个并发控制失败的例子
为了更好的理解并发控制的概念,我们先重复一个经常列举的例子。先给出银行数据库中一个经过简化的帐户表(account)信息的数据字典定义,以后我们都将会以此表作为示例。
列名称 列代码 列类型
帐户号 Id(键值列) Char(10)
户主 Uname Char(10)
存入金额 Mdeposit Currency
支出金额 Mpayout Currency
存款余额 Mbalance Currency
这个例子是在客户程序与服务器端数据库的会话过程中产生的:某户主代表在银行前台取款2,000元,银行出纳查询用户的存款信息显示银行存款余额20,000元;正在这时,另一银行帐户转帐支票支付该帐户5,000元,机器查询也得到当前用户存款20,000元,这时银行的出纳员看到用户存款超过了取款额,就支付了客户2,000元并将用户存款改为18,000元,然后银行的另一名操作员根据支票,将汇入的5,000元加上,把用户的余额改为25,000元。很明显银行将会损失2000元,因为另一个出纳员所做的修改被覆盖了。
这是由于对并发操作控制的失败造成的,由于没有对两个并发操作进行合理的隔离,对数据进行合理的锁定,导致经出纳员查询所得到的客户端数据集与数据库的数据出现不一致,结果便产生了丢失修改。
三.数据库并发控制理论基础
在此对并发控制中经常用到的概念略做解释,具体内容请读者查资料。事务是数据库中一个重要概念,它是一系列要么都做,要么都不做的程序集合,是数据库并发控制的单位。事务并发控制不当的话,可以产生丢失修改、读脏数据、不可重复读等数据不一致。但在应用中为了并发度的提高,可以容忍一些这样的不一致,例如大多数业务逻辑经适当的调整以后是可以容忍不可重复读的。当今流行的关系数据库系统(如oracle,sql server等)是通过事务隔离级别(TRANSACTION ISOLATION LEVEL)与封锁机制来定义并发控制所要达到的目标的,根据其提供的协议,我们可以得到几乎任何类型的合理的并发控制方式。例如,Microsoft sql server系统中有四种锁:共享锁,排它锁,意向锁(又分为共享意向锁,排它意向锁,共享意向排它锁),修改锁。各种锁之间有确定的相容关系。有四种事务隔离级别:未提交读、提交读、可重复读、串行化读,不同的隔离级别所规定的封锁协议不同。这一部分内容非常之丰富,足足可以写一本书,篇幅所限,不拟细述,有关内容请查阅数据库理论方面的教材。
封锁类型与隔离级别如此之丰富,那么选择时到底本着一个什么原则呢?那就是数据一致性要求与并发度两个方面。例如四种隔离级别数据一致性依次升高,但并发度依次降低,一般系统默认的隔离级别是提交读。一般来说这可以满足应用的要求了,但这种隔离级别不能避免不可重复读的现象,就是说在你浏览数据库记录的期间,不同时间读的同一条记录可以有不同的内容。有时需要动态的通过sql语句来改变其封锁状态或者隔离级别。
四.并发控制技术的实现途径
并发控制的实现途径有多种,如果DBMS支持的话,当然最好是运用其自身的并发控制能力。如果系统不能提供这样的功能,可以借助开发工具的支持,还可以考虑调整数据库应用程序,而且有的时候可以通过调整工作模式来避开这种会影响效率的并发操作。笔者对各种策略做了一个总结,主要有一下几点:
(一)调整工作模式,修改应用程序,避免不必要的并发。
这在某些情况下是可行的,例如规定录入人员只能修改自己所创造的记录,那么就不会出现并发操作中的各种错误,因为这时各个不同的用户所能更新的记录不会发生重合。这种情况下,需要在数据库表中增加用户列。在用户浏览记录时,将用户列作为一个过滤条件,对应用程序的sql语句做相应的调整。但这种策略的作用有限,因为在大量情况下,并发控制不可避免。
(二)借助于DBMS的功能。
大型关系系统都有比较好的并发控制功能。例如可以采用更新游标、显式加锁、更改事务隔离级别等等。当然在其使用方面有很多注意的技巧,如:(1)事务定义最好不要包含客户交互部分。(2)只有在数据一致性要求特别严格,但并发度要求不高的时候采用可重复读与可串行读的隔离级别。(3)在同一个事务当中,要适当根据需要来变更数据的锁定级别,但一般情况下不要用TABLOCK这样粗粒度的封锁。(4)不同事务之间可以根据并发度的需要来显式设定隔离级别。(5)在包含客户交互的操作中使用游标,并尽可能缩短交互时间。
我们看一个informix数据库中采用更新游标的例子。定义更新游标语法:DECLARE CURSOR-name CURSOR FOR SELECT-statement FOR UPDATE[OF column-list]。更新游标在完成数据的浏览和修改时,要对当前的记录隐式加锁,注意更新游标只对可更新视图有效。为了提高并发度,经常要结合滚动游标来使用,滚动游标定义方法:DECLARE CURSORname SCROLL CURSOR[with hold] FOR SELECTstatement),不过滚动游标不对当前的记录加锁。
下段代码完成客户对帐户内容的浏览和修改,代码采用informix的esql/c(以c语言作为宿主语言)来编写,展示了更新游标的使用方法:
$DECLARE mycurs CURSOR FOR SELECT Mdeposit,Mpayout,Mbalance FROM acount FOR UPDATE; //定义更新游标
$OPEN mycurs; //打开游标
for(;
{$FETCH mycurs INTO $ Mdeposit,$Mpayout,$Mbalance;// 从游标中读记录
if sqlcode=SQLNOTFOUND then exit;//如果记录取完,则退出循环
….//显示记录内容给用户
….//如果用户决定要修改记录,则继续执行
$UPDATE acount SET (Mdeposit,Mpayout,Mbalance)=
($Mdeposit,$Mpayout,$Mbalance) WHERE CURRENT of mycurs;//更新数值
}
带有FOR UPDATE的游标语句有加锁功能。看上面的代码,在进行FETCH操作以后,游标所指向的当前记录被加共享锁,当用户决定要修改时,将该记录上的锁提升为排它锁。那么此时其它用户不可以更新此记录。这种方法有个缺点,就是即使用户不对当前记录进行修改,也要对当前的记录加锁,影响了并发度,那么此时可以采用的一个方法是:(1)定义一个滚动游标来完成查询;(2)获取游标的一个记录,显示给用户;(2)用户浏览记录,直到要修改或者删除的记录;(3)当用户选择修改一个记录时,为用户想要修改的记录定义一个更新游标(4)使用更新游标获取记录,并重新显示锁定的记录;(5)更新这个记录。那么上面的程序就可以更改为:
$DECLARE mycurs SCROLL CURSOR for SELECT Id, Mdeposit,Mpayout,Mbalance FROM acount;//定义滚动游标
$OPEN mycurs//打开滚动游标
for(;
{$FETCH mycurs INTO $Id,$Mdeposit,$Mpayout,$Mbalance; //从游标中读取记录值
if sqlcode=SQLNOTFOUND then exit; //如果读完,退出循环
….//显示记录内容给用户看
….//如果用户决定要修改记录,则继续执行
$DECLARE mycurs_update CURSOR FOR SELECT Mdeposit,Mpayout,Mbalance
FROM Acount WHERE Id=$Id; //定义更新游标
$FETCH mycurs_UPDATE INTO $Mdeposit,$Mpayout,$Mbalance; //读取数值
$UPDATE acount SET (Mdeposit,Mpayout,Mbalance)= ( $Mdeposit,$Mpayout,$Mbalance)
WHERE CURRENT of mycurs_update; //更新数值
(三)利用开发工具的支持。
许多数据库开发工具都有一些方便的选项或部件来支持并发控制,而不论DBMS是否支持并发控制。我们看一下Delphi与Powerbuilder的并发控制方法。
Delphi是一个优秀的c/s开发工具,它用来查询数据的数据库控件是TQuery,它可以和TUpdatesql控件有机的结合起来完成数据库表数据的浏览和更新。其中在TQuery控件中有一个属性是Updatemod(修改模式),他有三种选择:(1)upWhereAll:在浏览和修改期间只要有人修改了此记录某个列,那么不管你是否修改过这个列,你的修改在提交时都不能成功。(2)upWhereChanged:只根据键值列和你已经修改的列来决定你的修改是否成功,如果别人所修改的本记录的列与你修改的列不相交,那么你的修改仍然是成功的。(3)UpWhereOnly:只根据键值是否修改来判断你的更新是否成功。
与TQuery控件配套使用的TUpdatesql控件根据所指定的修改属性,自动生成所需的更新语句,非常方便。第2种模式是最常用的修改模式,只要别人对记录所做的修改不与自己的重合,那么就会提交成功,这即保证不会发生数据的丢失、覆盖,并且具有较高的并发度。还是上边的例子,比如说客户浏览记录后修改的是记录中Mpayout、Mbalance两列,那么在修改选项upWhereChanged下,Tupdatesql控件所生成的SQL语句是:
UPDATE acount SET (Mpayout,Mbalance)=($Mpayout,$Mbalance)
WHERE Key=Key_old and Mpayout=Mpayout_old and Mbalance=Mbalance_old;
其中Key_old,Mpayout_old和Mbalance_old是delphi替用户所生成的中间变量,暂存原先数据记录的旧值,用于比较旧值与现在的值是否相等,如果不相等,说明已经有别的用户更改了该记录,那么为了避免丢失修改,该用户的更新操作不能完成,反之则可以完成。那么当出纳员修改帐户时,如果别人已经修改了这个帐户,那么他的这次修改是不成功的,必须重新刷新记录才可能成功修改。对上面的例子进行这种改造,就可以避免银行的损失。
与Delphi媲美的一个另一个工具是著名的Powerbuilder,在其DataWindows的设计中,我们选择菜单Rows|Update…,会出现Specify Update Characteristics的设置窗口,在这个窗口中我们设置Update语句中Where子句的生成,以此来进行并发控制。在这里有三个选项: (1)Key Columns:生成的Where子句中只比较表中的主键列的值与最初查询时是否相同来确定要修改的记录。与Delphi中的UpWhereOnly选项对应。 (2)Key and Updateable Columns:生成的Where子句比较表中主键列和可修改列的值与最初查询时否是相同。与Delphi的upWhereall相对应。 (3)Key and Modified Columns:与Delphi的upWhereChanged选项对应。Where子句比较主键和要修改的列。
(四)调整应用。
有的数据库没有提供并发控制的功能,例如Foxpro等,象Mysql的某些版本也不支持事务。而且有的开发工具(例如一些网页脚本编辑器等)也没有提供实现并发控制的部件,那么要实现并发控制,就只能借助于调整我们的应用程序和数据库结构的办法了。
可以按照封锁的基本思想来调整应用。在需要进行并发控制的数据库表中增加一个锁字段,这个字段可以是一个布尔型变量,为true则为锁定,为false则为空闲。此时表的结构变为:
列名称 列代码 列类型
帐户号 Id(键值列) Char(10)
户主 Uname Char(10)
存入金额 Mdeposit Currency
支出金额 Mpayout Currency
存款余额 Mbalance Currency
锁 Lock Boolean
如果一个应用中,客户查询这个表时,可以修改表的记录,那么为了防止别的客户在该用户编辑某记录期间修改这个记录,那么就需要客户在浏览到该记录的数据时,给该记录加上锁(即将锁字段改为true),修改完毕后释放锁(将锁字段改为false)。别的客户要修改这个表的记录的话就先检测一下该记录有没有被加锁,如果已经加锁,则不能进行修改。如果锁字段空闲,那么首先给该记录加锁,然后取记录给客户浏览、编辑,在此期间别的客户不能修改记录。这就很有效的防止了丢失修改。
上面是一种常用的方法,但是也不是完全没有缺点,它可能会产生这样一种副作用:当一个用户决定修改一个记录时,该记录被锁定,等待用户修改,但此时正好用户离开了,那么这条记录将一直被锁定直到用户提交(可能是几个小时之后了)或者会话超时,那么在这一段时间内别的用户就不可以更改这一条记录,导致并发度很低。有一个绕过这个问题的解决办法就是将浏览到的记录记到old_record(自定义的变量)中,再将old_record的内容拷贝到一个新记录new_record(自定义的变量)中,用户编辑new_record,当提交new_record时比较old_record和原先的记录,如果不一样,则表明已经有用户修改了原先的记录,此时把对new_record做的修改放弃;反之则将其内容提交。
上边的方法还有一个小小的不足:在做新旧记录的比较的时候必须比较整条记录,费时间而且程序写起来比较麻烦。可以考虑在原先的表中增添一个时间戳列(此时可以取消lock列),那么此时表的结构变为:
列名称 列代码 列类型
帐户号 Id(键值列) Char(10)
户主 Uname Char(10)
存入金额 Mdeposit Currency
支出金额 Mpayout Currency
存款余额 Mbalance Currency
时间戳 Date Datetime
当浏览记录时将时间戳值记到old_date(自定义变量)中,再将记录内容记到一个新记录new_record中,用户编辑new_record,当提交new_record时比较old_date和原先的记录中的时间戳,如果不一样,则表明已经有用户修改了原先的记录,此时把对new_record做的修改放弃;反之则将其内容提交。更新语句为:
UPDATE acount SET (Mpayout,Mbalance,Date)=($Mpayout,$Mbalance,$Date)
WHERE Key=Key_old and Date=$Date_old;
五.总结
我们通常说的并发控制是指在DBMS(数据库管理系统)内部进行的并发控制。而并发控制的方法非常丰富,远不止此。可以借助于数据库本身的能力,也可以利用开发工具,还可以通过调整自己的程序来实现。在数据库应用中,进行并发控制的方法、实现途径多种多样。在选择时所依据的基本原则就是:数据一致性一定要合乎应用的需要,在此基础上,尽量提高并发度。 |
回复
|
|
| 18楼 |
2005-01-19 15:33:00 |
积分:0分
|
数据库的查询优化技术 张在建 数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、政府等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
7.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(other column)
102,032Seageat 30G disk……
500,049Novel 10M network card……
……
2.vendor表
厂商号厂商名其他列
(vendor _num)(vendor_name) (other column)
910,257Seageat Corp……
523,045IBM Corp……
……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
……
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECT part_desc,vendor_name,part_amount
FROM part,vendor,parven
WHERE part.part_num=parven.part_num
AND parven.vendor_num = vendor.vendor_num
ORDER BY part.part_num
如果不建立索引,上述查询代码的开销将十分巨大。为此,我们在零件号和厂商号上建立索引。索引的建立避免了在嵌套中反复扫描。关于表与索引的统计信息如下:
表行尺寸行数量每页行数量数据页数量
(table)(row size)(Row count)(Rows/Pages)(Data Pages)
part15010,00025400
Vendor1501,000 2540
Parven13 15,000300 50
索引键尺寸每页键数量页面数量
(Indexes)(Key Size)(Keys/Page)(Leaf Pages)
part450020
Vendor45002
Parven825060
看起来是个相对简单的3表连接,但是其查询开销是很大的。通过查看系统表可以看到,在part_num上和vendor_num上有簇索引,因此索引是按照物理顺序存放的。parven表没有特定的存放次序。这些表的大小说明从缓冲页中非顺序存取的成功率很小。此语句的优化查询规划是:首先从part中顺序读取400页,然后再对parven表非顺序存取1万次,每次2页(一个索引页、一个数据页),总计2万个磁盘页,最后对vendor表非顺序存取1.5万次,合3万个磁盘页。可以看出在这个索引好的连接上花费的磁盘存取为5.04万次。
实际上,我们可以通过使用临时表分3个步骤来提高查询效率:
1.从parven表中按vendor_num的次序读数据:
SELECT part_num,vendor_num,price
FROM parven
ORDER BY vendor_num
INTO temp pv_by_vn
这个语句顺序读parven(50页),写一个临时表(50页),并排序。假定排序的开销为200页,总共是300页。
2.把临时表和vendor表连接,把结果输出到一个临时表,并按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num
FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
这个查询读取pv_by_vn(50页),它通过索引存取vendor表1.5万次,但由于按vendor_num次序排列,实际上只是通过索引顺序地读vendor表(40+2=42页),输出的表每页约95行,共160页。写并存取这些页引发5*160=800次的读写,索引共读写892页。
3.把输出和part连接得到最后的结果:
SELECT pvvn_by_pn.*,part.part_desc
FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
这样,查询顺序地读pvvn_by_pn(160页),通过索引读part表1.5万次,由于建有索引,所以实际上进行1772次磁盘读写,优化比例为30∶1。笔者在Informix Dynamic Sever上做同样的实验,发现在时间耗费上的优化比例为5∶1(如果增加数据量,比例可能会更大)。
小结
20%的代码用去了80%的时间,这是程序设计中的一个著名定律,在数据库应用程序中也同样如此。我们的优化要抓住关键问题,对于数据库应用程序来说,重点在于SQL的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。 |
回复
|
|
| 19楼 |
2005-01-19 15:34:00 |
积分:0分
|
文档数据库与关系数据库的比较 北京航空航天大学1-53信箱 蒋洪迅(jianghx@990.net)
摘要:随着应用领域的不断拓展和多媒体技术, 人们发现关系数据库的许多限制和不足,因而数据库技术进入了"后关系数据库时代"。文档数据库由此应运而生。本文概要地从数据格式、数据库结构和WEB发布数据三个方面比较了文档数据库与关系数据库的异同,同时区别了文档数据库与过去存储数据的文件系统的不同。
关键词:数据库 Internet 文档数据库 WEB数据库服务器
一. 关系数据库的优势和目前面临的问题
从60年代末开始, 数据库技术经历了层次数据库、网状数据库和关系数据库而进入数据库管理系统( DBMS)阶段至今, 数据库技术的研究也不断取得进展。80年代, 关系数据库成为发展的主流, 几乎所有新推出的DBMS产品都是关系型的。关系型数据库在计算机数据管理的发展史上是一个重要的里程碑,这种数据库具有数据结构化、最低冗余度、较高的程序与数据独立性、易于扩充、易于编制应用程序等优点,目前较大的信息系统都是建立在结构化数据库设计之上的。
然而,随着网络技术和软件技术的飞速发展,特别是Internet和Intranet技术的发展,使得非结构化数据的应用日趋扩大。关系数据库从1970年发展至今,虽功能日趋完善,但对数据类型的处理只局限于数字、字符等,对多媒体信息的处理只是停留在简单的二进制代码文件的存储。然而,随着用户应用需求的提高、硬件技术的发展和Intranet/Internet提供的多彩的多媒体交流方式,用户对多媒体处理的要求从简单的存储上升为识别、检索和深入加工,正是用户呼唤出"通用"数据库服务器来处理占信息总量70%的声音、图像、时间序列信号和视频等复杂数据类型。据有关数据,1996年,全球数据库市场的销售额已接近40亿美元,预计到2000年,数据库市场销售额将达到80亿美元。巨大的增长潜力来自复杂数据类型的处理需求,使处理复杂数据类型的"超媒体"数据库将成为各公司投资研发的重点。
二. 全新的"文档数据库"概念
从1989年起,Lotus通过其群件产品Notes提出了数据库技术的全新概念-"文档数据库",文档数据库区别于传统的其它数据库,它是用来管理文档。在传统的数据库中,信息被分割成离散的数据段,而在文档数据库中,文档是处理信息的基本单位。一文档可以很长、很复杂、可以无结构,与字处理文档类似。
文档数据库与五、六十年代管理数据的文件系统不同,文档数据库仍属于数据库范畴。首先,文件系统中的文件基本上对应于某个应用程序。当不同的应用程序所需要的数据有部分相同时,也必须建立各自的文件,而不能共享数据,而文档数据库可以共享相同的数据。因此,文件系统比文档数据库数据冗余度更大,更浪费存储空间,且更难于管理维护。其次,文件系统中的文件是为某一特定应用服务的,所以,要想对现有的数据再增加一些新的应用是很困难的,系统不容易扩充。数据和程序缺乏独立性。而文档数据库具有数据的物理独立性和逻辑独立性,数据和程序分离。
文档数据库也不同于关系数据库,关系数据库是高度结构化的,而Notes的文档数据库允许创建许多不同类型的非结构化的或任意格式的字段,与关系数据库的主要不同在于,它不提供对参数完整性和分布事务的支持,但和关系数据库也不是相互排斥的,它们之间可以相互交换数据,从而相互补充、扩展。
三. 关系数据库和文档数据库的异同
下面从三个方面比较两种数据库的异同:
1. 格式文本/多媒体(Rich Text/Multimedia) 在信息时代,所有信息大体上可以分为两类:一类信息能够用数据或统一的结构加以表示。例如,成本、费用、人员、工资以及员工业绩等,都可以用数字或文字来描述或表达。这类信息具有相同的层次或网络结构,我们称之为结构化数据;而另一类信息根本无法用数字或者统一的结构表示,例如,图象、声音等,我们称之为非结构化数据。这些非结构化数据既可以是一段包含大量附加信息(如排版信息)的文字信息,也可能是一段声音、图象,甚至是影像。对于结构化数据和非结构化数据的关系如图所示,非结构化数据包括结构化数据,但又不止是结构化数据;结构化数据属于非结构化数据,是非结构化数据的特例。一般来说,在人们的认识之中,关系数据库最主要的特征就是数据的结构化。然而,随着网络技术和软件技术的飞速发展,特别是Internet和Intranet技术的发展,使得非结构化数据的应用日趋扩大。关系数据库是高度结构化的,这种数据结构化使关系数据库具有冗余度最低、程序与数据独立性较高、易于扩充、易于编制应用程序的特点。但是,随着应用领域的不断拓展, 为满足应用对数据处理不断"苛刻"的要求, 人们开始发现关系数据库的许多限制和不足。因为文档数据库的基本元素就是文档本身,而数据库中的文档可以同时包含结构化的和非结构化的信息,所以,文档数据库能够存储和管理类似文档这样的非结构化数据。特别是,Notes的对象库是一个非常理想的商业信息存储器,可用于高效地存储、传播、分配和管理这类信息。这类信息通常具有丰富的数据类型,如表格(可以是从某个关系数据库或电子表软件中得到的)、格式化文本、WWW的页面、图形、OLE对象、或扫描的图象以及传真件、声频或视频信号这样的多媒体信息。
2. 在结构上 关系数据库是应用数学方法来处理数据库数据的,其数据模型也是建立在数学概念基础上的,在关系模型中,数据在用户观点下的逻辑结构就是一张二维表。而Lotus Notes是一个文档数据库管理系统,文档数据库的基本元素就是文档。这里的文档和关系数据库中的记录相似。Notes文档的结构是由表单(form)定义的,而表单由一组各式各样的字段域组成。
· 域(Field)、域(Domain)和字段(Field)
在关系数据库中,域(Domain)是属性值的集合,如:大于0小于150的正整数,长度小于25的字符串集合等等。而在文档数据库中,域(Field)的概念出现在表单对象中,域是表单上存储数据的单个元素,域决定了一个独立的文档能包含什么数据。虽二者的中文译文相同,但其实际意义和作用相差千里。反而在文档数据库中域的概念与关系数据库中的字段(Field)相类似。
· 条目(Item)和属性(Attribute)
在关系数据库中,有属性(Attribute)的概念,即二维表中的每一列称为一个属性,给每一列起一个名称即属性名。而在文档数据库中,就没有"属性",文档拥有的是条目(Item),条目是指存储于文档中的任意数据部分。每一个条目代表文档中一段数据,在用户界面中,是通过表单中的域来显示文档中的条目的。二者虽命名不同,但笔者认为从"属性"的角度更容易理解文档数据库中条目的意义。
· 视图(View)
在关系数据库和文档数据库中,均有视图的概念。在关系数据库中,视图是指从一个或几个基本表(或视图)导出的表。视图和基本表不同,视图是一个虚表,即视图所对应的数据不实际存储在数据库中,数据库中只存储视图的定义(存在数据字典中)。在文档数据库中,用户通过Notes视图浏览文档。视图是很方便的目录,用户可以从中看到关于文档的概要信息和文档的状态,然后存取特定的Notes文档。文档数据库中文档可以在视图中显示起全部或部分内容,通过视图,用户可以看一组文档的关键域,并可按某一准则对显示的信息进行分类和排序。实质上,二者逻辑上非常相似,即视图所对应的数据均不实际存储在数据库中,数据库中只存储视图的定义。
3. WEB数据库 近年来网络体系结构经历了一次次重大变迁,客户机/服务器结构越来越流行了,Intranet以惊人的速度在短短一年内迅速蔓延,面临新的格局,如何简易地实现"客户机/服务器"连接与"Web/Intranet"连接成为数据库管理人员关注的焦点。计算机技术和网络技术的发展使以网络为中心的计算日益得到重视,WWW系统和数据库成为网络化信息服务的基础。如果能把文档数据库同WWW服务器连接起来,就可以从浏览器中检索文档。这种一体化的信息网络系统:数据库+WWW服务器,便成为下一代Internet开发的新领域。目前,在Web上一个典型的数据库应用程序应包括三个部分:Web浏览器、HTTP 服务器和Web数据库服务器。对于一般的关系型数据库,实现Web数据库的应用通常有两种方法:一种是Web服务器提供中间件连接Web服务器与数据库服务器;另一种是把应用程序下载到客户端直接访问数据库。最常用的中间件技术有通用网关接口(CGI)和应用程序编程接口(API)两种。CGI程序缺陷在于CGI接口不支持用户与数据库间的持续互操作,再就是CGI的效率低,且需要同时运行多个CGI程序。用户每次连至服务器时,服务器都要创建一个事务或运行CGI程序的一个拷贝。这样对于一个复杂的应用或有多个用户访问时,就会加重服务器的负担。API在很大程度上克服了CGI的缺点,但其兼容性差和开发难度大也让广大开发人员望而却步。客户端访问数据库主要包括Java Script和数据库连接器(IDC)等。用Java Script和IDC开发Web数据库虽简单但同时也存在功能有限、不能完全控制其过程等缺点。所以它们也无法胜任较复杂的Web数据库的开发。难道就没有一个开发相对容易、功能强大且高效的Web数据库的开发方法吗?有,答案是-Lotus Notes。Lotus Notes通过文档数据库实现了多媒体文档管理,支持客户机/服务器工作方式。每个数据可以包含若干不同形式的Notes文档。存放在一个或多个Notes服务器上,可以让许多用户存取的数据库称为共享数据库。通过利用服务器存取和数据库存取控制等安全措施,数据库管理员可以规定谁可以存取数据库和在什么范围内使用数据库,各数据库在网上通过复制功能实现同步。这样,通过文档数据库用户可以在网络上存取、追踪储存和组织信息。
四. 结束语
最近几年,我国的数据库市场飞速发展,并将具有更为巨大的增长潜力,表现为如下特点:首先,在近五年里,用户的选型观念发生了明显的改变,有更多的用户把数据库的重要性放在十分重要的地位,其主要原因是用户将把应用软件和应用需求放在首位,应用软件是否满足用户需求是整个项目是否成功的标志,而应用软件开发直接依赖于数据库开发工具。实际上,大量潜在的投资都在应用软件的开发上;另一个原因是硬件随着芯片技术的发展越来越缺乏特性,硬件指标将变成次要的考虑因素。对行业性应用来讲,以前是制定几家硬件产品作为优选,而今可能是首选数据库厂家再考虑硬件厂家了。正是用户需求的这种变化给数据库厂商提供了新的发展机会。Lotus Notes为各数据库系统集成商提供了强大的文档数据库开发平台,Notes结合了企业级电子邮件、分布式文档数据库与快速应用开发等三位一体的强大技术、完全集成了Internet技术,提供用户完整的,以网络为中心的应用技术平台。可以预言,在不久的将来,Lotus Notes的文档数据库将成为继层次数据库、网状数据库和关系数据库之后的又一热点技术。
参考文献:
1. 萨师煊 王珊《数据库系统概论》
2. 《刍议数据库技术的最新发展》,微电脑世界,1997年第三期,"信息时代" |
回复
|
|
| 20楼 |
2005-01-19 15:36:00 |
积分:0分
|
利用数据库复制技术 实现数据同步更新
复制的概念
复制是将一组数据从一个数据源拷贝到多个数据源的技术,是将一份数据发布到多个存储站点上的有效方式。使用复制技术,用户可以将一份数据发布到多台服务器上,从而使不同的服务器用户都可以在权限的许可的范围内共享这份数据。复制技术可以确保分布在不同地点的数据自动同步更新,从而保证数据的一致性。
SQL复制的基本元素包括
出版服务器、订阅服务器、分发服务器、出版物、文章
SQL复制的工作原理
SQL SERVER 主要采用出版物、订阅的方式来处理复制。源数据所在的服务器是出版服务器,负责发表数据。出版服务器把要发表的数据的所有改变情况的拷贝复制到分发服务器,分发服务器包含有一个分发数据库,可接收数据的所有改变,并保存这些改变,再把这些改变分发给订阅服务器
SQL SERVER复制技术类型
SQL SERVER提供了三种复制技术,分别是:
1、快照复制(呆会我们就使用这个)
2、事务复制
3、合并复制
只要把上面这些概念弄清楚了那么对复制也就有了一定的理解。接下来我们就一步一步来实现复制的步骤。
第一先来配置出版服务器
(1)选中指定[服务器]节点
(2)从[工具]下拉菜单的[复制]子菜单中选择[发布、订阅服务器和分发]命令
(3)系统弹出一个对话框点[下一步]然后看着提示一直操作到完成。
(4)当完成了出版服务器的设置以后系统会为该服务器的树形结构中添加一个复制监视器。同时也生成一个分发数据库(distribution)
第二创建出版物
(1)选中指定的服务器
(2)从[工具]菜单的[复制]子菜单中选择[创建和管理发布]命令。此时系统会弹出一个对话框
(3)选择要创建出版物的数据库,然后单击[创建发布]
(4)在[创建发布向导]的提示对话框中单击[下一步]系统就会弹出一个对话框。对话框上的内容是复制的三个类型。我们现在选第一个也就是默认的快照发布(其他两个大家可以去看看帮助)
(5)单击[下一步]系统要求指定可以订阅该发布的数据库服务器类型,SQLSERVER允许在不同的数据库如 ORACLE或ACCESS之间进行数据复制。但是在这里我们选择运行"SQL SERVER 2000"的数据库服务器
(6)单击[下一步]系统就弹出一个定义文章的对话框也就是选择要出版的表
(7)然后[下一步]直到操作完成。当完成出版物的创建后创建出版物的数据库也就变成了一个共享数据库。
第三设计订阅
(1)选中指定的订阅服务器
(2)从[工具]下拉菜单中选择[复制]子菜单的[请求订阅]
(3)按照单击[下一步]操作直到系统会提示检查SQL SERVER代理服务的运行状态,执行复制操作的前提条件是SQL SERVER代理服务必须已经启动。
(4)单击[完成]。完成订阅操作。
完成上面的步骤其实复制也就是成功了。但是如何来知道复制是否成功了呢?这里可以通过这种方法来快速看是否成功。展开出版服务器下面的复制——发布内容——右键发布内容——属性——击活——状态然后点立即运行代理程序接着点代理程序属性击活调度把调度设置为每一天发生,每一分钟,在0:00:00和23:59:59之间。接下来就是判断复制是否成功了打开C:\Program Files\Microsoft SQL Server\MSSQL\REPLDATA\unc\XIAOWANGZI_database_database下面看是不是有一些以时间做为文件名的文件夹差不多一分中就产生一个。要是你还不信的话就打开你的数据库看在订阅的服务器的指定订阅数据库下看是不是看到了你刚才所发布的表 --一个手工同步的方案 --定时同步服务器上的数据 --例子:
--测试环境,SQL Server2000,远程服务器名:xz,用户名为:sa,无密码,测试数据库:test
--服务器上的表(查询分析器连接到服务器上创建)
create table [user](id int primary key,number varchar(4),name varchar(10))
go
--以下在局域网(本机操作)
--本机的表,state说明:null 表示新增记录,1 表示修改过的记录,0 表示无变化的记录
if exists (select * from dbo.sysobjects where id = object_id(N'[user]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [user]
GO
create table [user](id int identity(1,1),number varchar(4),name varchar(10),state bit)
go
--创建触发器,维护state字段的值
create trigger t_state on [user]
after update
as
update [user] set state=1
from [user] a join inserted b on a.id=b.id
where a.state is not null
go --为了方便同步处理,创建链接服务器到要同步的服务器
--这里的远程服务器名为:xz,用户名为:sa,无密码
if exists(select 1 from master..sysservers where srvname='srv_lnk')
exec sp_dropserver 'srv_lnk','droplogins'
go
exec sp_addlinkedserver 'srv_lnk','','SQLOLEDB','xz'
exec sp_addlinkedsrvlogin 'srv_lnk','false',null,'sa'
go --创建同步处理的存储过程
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_synchro]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_synchro]
GO
create proc p_synchro
as
--set XACT_ABORT on
--启动远程服务器的MSDTC服务
--exec master..xp_cmdshell 'isql /S"xz" /U"sa" /P"" /q"exec master..xp_cmdshell ''net start msdtc'',no_output"',no_output --启动本机的MSDTC服务
--exec master..xp_cmdshell 'net start msdtc',no_output --进行分布事务处理,如果表用标识列做主键,用下面的方法
--BEGIN DISTRIBUTED TRANSACTION
--同步删除的数据
delete from srv_lnk.test.dbo.[user]
where id not in(select id from [user]) --同步新增的数据
insert into srv_lnk.test.dbo.[user]
select id,number,name from [user] where state is null --同步修改的数据
update srv_lnk.test.dbo.[user] set
number=b.number,name=b.name
from srv_lnk.test.dbo.[user] a
join [user] b on a.id=b.id
where b.state=1 --同步后更新本机的标志
update [user] set state=0 where isnull(state,1)=1
--COMMIT TRAN
go --创建作业,定时执行数据同步的存储过程
if exists(SELECT 1 from msdb..sysjobs where name='数据处理')
EXECUTE msdb.dbo.sp_delete_job @job_name='数据处理'
exec msdb..sp_add_job @job_name='数据处理' --创建作业步骤
declare @sql varchar(800),@dbname varchar(250)
select @sql='exec p_synchro' --数据处理的命令
,@dbname=db_name() --执行数据处理的数据库名 exec msdb..sp_add_jobstep @job_name='数据处理',
@step_name = '数据同步',
@subsystem = 'TSQL',
@database_name=@dbname,
@command = @sql,
@retry_attempts = 5, --重试次数
@retry_interval = 5 --重试间隔 --创建调度
EXEC msdb..sp_add_jobschedule @job_name = '数据处理',
@name = '时间安排',
@freq_type = 4, --每天
@freq_interval = 1, --每天执行一次
@active_start_time = 00000 --0点执行
go
|
回复
|
|