楼主: ccwlm741212

[精华] Ms SQL常见问题收集整理集(不断收集中...)

[复制链接]
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
21#
 楼主| 发表于 2003-11-23 12:53 | 只看该作者
触发器的几种应用

李振华 李贵龙 刘涯

  摘 要 列举了触发器的几种代表性应用:数据分散—集中式模型的设计,历史数据的导出,应用系统间的数据接口。并对如何设计这些触发器进行了探讨。
  关键词 触发器,数据分散—集中模型,历史数据导出,数据接口

1 引言
  在大型数据库设计中,会经常用到触发器。它的特点是:一旦被定义,就存在于后台数据库系统(server,服务器方)中,并会在相应条件下自动地隐式执行,从而使得它的设计既与前台(client,客户机方)的平台无关,又免除了前台相关的数据操作设计。
  在文献[1]中,列举了触发器的几种应用:审计;复杂的完整性约束;复杂的安全性授权;事件登录;列值导出;分布式数据库中表复制。

2 触发器的另外几种应用
2.1 数据分散——集中式模型设计
  在实际开发过程中,经常遇到这样的数据维护要求:单位由多个部门组成,要求各部门只能维护本部门的数据,但另一方面,又需要将分散到各部门的数据集中起来进行汇总,得到本单位的汇总数据。如一个学校有多个系,学校需要各系的成绩汇总;一个工厂有多个生产车间,工厂需要各车间的产量汇总;一个公司有多个销售部门,公司需要各部门的销量汇总等等。
  在这种情况下,如果不使用触发器的话,数据库设计就存在困难:
  . 如果为每个部门都建立一个表,显然难以得到汇总的数据(在这种情况下,无法利用视图机制);
  . 如果所有的部门都共享一个表的话(这时,这张表中的数据实际就是汇总的数据),因为每个部门需要维护数据,所以都对这个表有修改权,因此在数据安全上难以控制。
  使用触发器的话,上述问题便可迎刃而解:为每个部门建立一个表(该部门的所有权限只限于对此表有修改权),再为汇总数据也建立一个表,然后在每个部门表上建立触发器,使得部门表上有数据更新时,便会对应地更改汇总表中的相关数据(见图1)。



图1 触发器应用于数据分散——集中式模型

  在这种模型中,要注意设计好部门表相关字段的完整性约束,使各部门表内的数据是唯一的,以防止不同部门表出现相同的数据记录,从而导致在汇总表中出现混乱。

2.2 历史数据导出
  数据库中的表只记载最新的数据,而不记载历史数据。但在很多情况下,历史数据的记载与分析反而比现实数据更有意义(这也正是数据仓库与数据库的区别之一),比如学校中学号的变动,工厂定额的更改,公司产品和原材料价格的变化、股票的升跌等等,它们都需要记录历史数据。
  如何使数据库也能记载历史数据呢?使用触发器可以解决这类问题。
  建立这类触发器的步骤是:建立数据表后,再建立对应的历史表(一般而言,历史表在字段组成上是数据表的超集,即在原数据表字段上再增加有关时间的字段),然后在两者之间设立触发器(见图2)。这样,每当数据表有数据变动,触发器便将变动的数据记入历史数据表中,从而达到自动记录历史数据的目的。



图2 历史数据的导出模式

2.3 应用系统间的数据接口
  一个完整的信息系统的建设一般不是一步到位的,往往是分期分批完成,而不同期次的系统往往又会有数据传递,然而由于需求发生变化或是其他原因,不同期次系统的数据库设计在表结构甚至字段上的设计都可能会互不一致(即使是在同一期的开发过程中,由于总体设计或数据字典方面的偏差或不足,或者需要集成多家系统,这种现象也会经常出现)。在不可能重建这些系统的情况下,它们之间的数据能无缝传递吗?换言之,它们之间能够做到无缝连接吗?

  在这种情况下,触发器可以是一种较好的解决方式:建立中间表,中间表的设计符合需方应用系统的设计格式,而它的数据又与供方应用系统的数据保持一致。(见图3)



图3 中间作为不同应用系统间的数据接口

  要注意的一点是:图示应用系统间的数据是单向流动的(即数据传递);如果数据需要双向流动(即数据交换),那么在触发器设计中应有退出机制,以避免发生触发器的递归。

3 结语
  触发器对数据库开发过程中遇到的问题,往往会有独到的解决方法。触发器能使数据库的设计变得简洁和高效。文中的3个例子,代表了触发器3个方面的典型应用。

作者简介:李振华 硕士,讲师。现从事计算机网络及数据库的教学与科研工作。

作者单位:中国地质大学网络中心 湖北.武汉(430074)

参考文献
[1] 沈佩娟,汤荷美,编著. 数据库管理及应用开发(第1版).北京:清华大学出版社,1995,6

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
22#
 楼主| 发表于 2003-11-23 12:54 | 只看该作者
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-EN?ABLE,0)
    其中,hStmt是一有效的语句句柄,常数SQL-ASYNC-ENABLE是所要设置的选项,参数0表示该选项(即异步执行模式)关闭。如果iRetCode返回SQL-SUCCESS,则表示语句执行状态已被设置为同步执行模式。
②利用ODBC2.0API,将语句执行状态设置为异步执行模式,然后在程序中不断查询一个操作语句是否已经执行完毕。
    ODBC2.0API中共有20多个函数支持异步执行,如上页表所示。
    这些函数第一次调用后,将返回值SQL-STILL-EXE?CUTING,这时应用程序将继续执行后续语句。过了一段时间后,应该再次调用原函数,而且要注意:实参数应传入与第一次调用时相同的语句句柄,其他参数也应一样(但会被忽略)。如果函数返回值为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,而只需使用数据控制项和数据库对象变量来编写应用程序,使开发效率大大提高,但程序运行速度比不上异步执行模式的速度。
    异步执行模式虽然在编程序时十分复杂,但在这种模式下可以进行多任务并行执行,使执行效率大大提高。
    我们在编制应用程序时,应根据自身的情况,对这两种模式的使用进行划分,以便既提高程序运行的安全可靠性,又提高程序执行的效率。

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
23#
 楼主| 发表于 2003-11-23 12:55 | 只看该作者
大型数据库设计原则
南昌航空工业学院电子工程系
喻金科
---- 一个好的数据库产品不等于就有一个好的应用系统,如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能。一般来讲,在一个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

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
24#
 楼主| 发表于 2003-11-23 12:56 | 只看该作者
客户/服务器远程数据传输处理技巧

---- 在 实 际 的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。

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
25#
 楼主| 发表于 2003-11-23 12:57 | 只看该作者
优化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';

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
26#
 楼主| 发表于 2003-11-23 12:58 | 只看该作者
提高客户机/服务器应用系统性能的一些方法

      作者:陈霞 罗铁坚


摘 要 客户机/服务器的应用逻辑设计是否合理,直接影响到客户机/服务器方式应用系统的处理性能。本文以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.

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
27#
 楼主| 发表于 2003-11-23 12:58 | 只看该作者
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

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
28#
 楼主| 发表于 2003-11-23 13:00 | 只看该作者
三层次数据库应用中的口令权限管理方案与实现

胡彤声

  摘 要:一种新型的基于三层次数据库应用思想的口令权限管理方案及其在高速公路收费数据库管理系统中的实现方法。
  关键词:三层次数据库 加密 口令权限

  本文针对一个安全性要求很高的管理信息系统——高速公路大型收费数据库管理系统,提出了一种新型的基于三层次的口令权限管理方案。收费站服务器中的数据库采用SQL SERVER6.5,前台用Visual FoxPro 5.0开发用户界面应用程序,用于财务数据查询、收费员交班管理、财务报表打印、通行票证管理、系统维护等业务操作,用Visual C++5.0开发后台数据通信应用程序,用于和车道计算机通信并将收费数据录入数据库。收费站用户可以使用Visual FoxPro 5.0环境、Isql环境等直接与数据库相联,也可通过应用程序登录数据库,还可登录到服务器上对数据库系统进行操作,收费分中心和中心用户通过市话网或ISDN专线远程访问服务器登录数据库。由于数据库中存放的是有关高速公路通行费收入的统计数据,其重要性是不言而喻的。为了有效地规范各类用户的操作权限,保证系统的安全,需要一套可靠的数据安全性管理方案。
1 系统设计思想
1.1 二层数据库应用设计存在的问题
  目前国内的大型数据库应用系统多采用客户/服务器(Client/Server)应用结构,其口令管理多为1个用户或组有1个口令,用户输入口令后即可直接对数据库进行操作。这种工作方式的应用系统实质上是基于二层的数据库应用模式,是直接面向数据的结构。其优点是结构简单、易于实现,但其有明显的不足:(1)安全性差:通过各类人员的口令确定其权限,泄密可能性大,可能出现非法人员登录。(2)数据完整性差:对数据的直接操作有可能造成异常情况中断操作时数据不完整。(3)效率低:各类用户随机的以单条的SQL语句直接提交给SQL SERVER,加重了网络的负担。
1.2 三层次数据库应用系统设计
  三层次的数据库应用结构是指“客户-应用-服务器”(Client/Application/Server)的三层次应用结构,其结构见图1。


图1  三层次数据库应用结构

  数据库的三层次应用结构的特点是将用户和服务器
中SQL SERVER之间的直接操作断开,在二者之间增加1个中间应用层,它的主要任务是将用户的所有操作请求通过Isql操作集,加工成完整的SQL事件(Transction),提交给数据库管理系统(DBMS)。1个事件可能包含1次请求中的多种数据库操作,而只有这一事件中的多种操作都成功完成时才返回事件完成标志,否则将复原所有操作并返回失败标志。三层次结构的优点是:(1)数据保密性好:数据库只需给中间应用层程序口令权限,而且口令有专门的加密、解密算法控制,极难破解。这样即使有人盗用合法用户的口令,也不能进行应用程序不允许的非法操作。(2)易于人员管理:虽然各类人员拥有登录计算机的口令,但不能直接登录数据库。(3)数据完整性好:各类用户对数据库的操作请求,被应用层加工成了SQL事件,易于保证数据的完整性。(4)网络资源运用合理:由于SQL事件的引入,取代了以往随机的杂乱的单条数据库操作指令,减轻了网络负担。
2 方案实现
  由于用户没有直接使用数据库的权限,所以本系统的设计重点在于应用程序的口令权限设置。它主要包括:口令加密算法的选择和应用程序对口令的具体操作方法。
2.1 口令加密算法的选择
  口令明文按选定的加密算法加密存储后,口令设置者以外的任何人,既使看到口令密文,也很难破译成明文,这就需要选择一套良好的密码体制。现有的密码体制分3类:传统密码体制(如Vigenere密码,Caesar密码等)、公开密码体制和数据加密标准(DES)体制。由于本系统主要要求口令的加密,所以我们选用数据加密标准(DES)体制。它是美国国家标准局公布的联邦加密标

准,是重复使用移位变换和替换变换的强块密码(强分组密码)。将明文按64位分组,在64位密钥的控制下,将每组明文转换为等块长的64位密文输出。DES本质上属于一种抗破译能力较强的乘积密码体制。
2.2 应用程序对口令的具体操作方法
  1.在服务器端,系统超级用户输入应用程序的ID号和口令,口令设置程序按所选加密算法将其加密后,存放在数据库的1个表(Table 1)中,然后只将对这个表的访问权授权给宾客用户(Guest)。
  2.在客户端,应用程序以Guest的身份,读取Table 1中对应本程序ID号的口令密文,然后按相应解密算法将其转换为明文,用此明文即可登录数据库进行权限内的操作。
  3.当由于某种原因需要改变某个应用程序的口令时,系统超级用户需输入此应用程序的ID号和原口令,才可设置新的口令。
3 结束语
  三层次数据库应用方法是对以往两层次数据库应用方法的改进和优化,是数据库应用不断发展的必然趋势。本文讨论的口令权限管理方案是对三层次数据库应用系统的初步尝试。该方案已用于312国道合—宁段高速公路大型收费数据管理系统中,在实践中经过各种复杂情况的考验,逐步完善并得到用户的好评。

作者单位:石家庄电子部五十四所(050081)

参考文献

1 美国运输联邦公路局.交通控制系统手册.北京:人民交通出版社,1998

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
29#
 楼主| 发表于 2003-11-23 13:02 | 只看该作者
一种安全和限制查询的程序设计思想

洪国瑞

一、 引言
  在电脑处理程序中,根据操作员的级别,赋予不同的权限,执行相应的操作,是一个基本要求。这就是通常的安全性问题。另外,在一个单位内部,由于人员的级别不同,信息查询的范围不同,如在一个学校中,校长要能够查询全校信息,教研室主任只能查询本教研室信息,教师只能查询自己的信息,这个问题我们称为限制查询问题。实际上,这两个问题是相互关联的。在程序开发中,可分两步解决。

二、 安全性程序设计思想
  首先,建立两张表,一张为operator,用于存放操作员工号和姓名。
  Create Table operator(
   opCode Char(6),
   opName Char(8),
  )
  以下是表中的部分数据:
  000000  管理员
  230010  张三
  ……  ……
  另一张表为permissionContro1,用于存放操作员权限控制表。
  Create Table permissionControl(
    opCode Char(6),
    permissionName Char(20),
  )
  以下是表中的部分数据:
  230010  录入
  230010  查询
  230010  统计
  ……  ……
  在要进行权限控制的函数中,加上权限控制检验函数,进行权限控制。
  函数名: checkPermission
  入 口: opCode   操作员代码
       permissionName  权限名称
  出 口: Ture   具有权限
       False   不具有权限
  函数checkPermission 的功能就是从表permissionContro1 中,查找是否有opCode 等于入口参数opCode,permissionName 等于入口参数permissionName 的记录,如有,则返Ture ,否则返回False。
  如在录入函数中,
  Function inputMainTable()
  If checkPermisson(p-opCode,“录入”)=False Then
   Error“无此权限”
   Return
  End If
  ……
  程序主体
  ……
  End Function

三、 限制查询程序设计思想
  限制查询程序的设计思想是在上面的安全思想基础上进行扩充。
  1.在人员编码中,考虑人员的级别,人员所属部门,即进行规则编码。
  人员编码为6位,分三个部分:
  第一部分,第一位,表示人员的级别:
  0  校长
  1  副校长
  2  教研室主任
  3  教研室副主任
  4  教师
  第二部分,第二、三两位,科室编码。
  第三部分,人员流水号。
  这种人员编码,加进了人员的级别、科室的编码,其目的是为了从人员的编码中,可以方便地判断出人员的级别、所属科室,从而为限制查询提供必要的信息。
  2.在数据表中,增加业务员代码(ywyCode)字段。
  3.在输入查询条件后,调用函数getQueryStrings(opCode)返回一字符串。
  4.将返回的字符串与原来的查询条件AND连接,成为新的查询条件,按这个条件从数据库中Select数据,将满足上述业务需求。
  以下用Informix-4GL语言,实现上述设计思想的主要语句:
  Construct By Name qs1 On mainTable.*
  Let qs2=“Select*From mianTable Where”,qs1 Clipped
  Let queryString=getQueryStrings(p_opCode.opCode)
  Let qs2=qs2 Clipped, “And”, queryStrings Clipped
  Prepare preQuery From qs2
  Declare curQuery Cursor For preQuery
  Foreach curQuery Into p-policy.*
  #此处是对满足条件的每一条记录的处理
  End Foreach
  函数getQueryStrings
  入口参数: opCode 操作员代码
  出口参数: strings 根据不同的操作人员,返回不同的查询条件字符串
  Function getQueryStrings(t_opCode)
  Define t_opCode Char(3)
  Define queryStrings Char(100)
  Case t_opCode[1,1]
  When “0”
  When “1”
   Let queryStrings=“1=1”
  When “2”
  When “3”
   Let queryStrings=“ywyCode[2,3]=
   \'''',t_opCode[2,3],''\''''
  When “4”
  When “5”
   Let queryStrings=“ywyCode=\'''',t_opCode,''\''''
  End Case
  Return queryStrings
End Function
  在函数中,当操作员代码第一位为0或1时,返回1=1,即对查询条件不作任何附加限制。当操作员代码第一位为2或3时,返回ywyCode的第二,第三位等于opCode的第二,第三位。根据编码规则,此操作员为教研室主任,因此限制只能查询本科的。当操作员代码第一位为4或5时,返回ywyCode 等于opCode。根据编码规则,此操作员为业务外勤,因此限制只能查询自己经手的业务。
  在程序调试中,如觉得不方便,可以在get QueryStrings函数中,始终返回“1=1”。
  以上两个程序设计思想中,opCode是操作员登录的操作员代码。

作者单位:江苏省盐城市卫生学校(江苏盐城224001)

使用道具 举报

回复
求职 : 系统分析师
论坛徽章:
691
博彩大赢家
日期:2014-07-14 11:41:47博彩大赢家
日期:2015-09-24 12:11:05菠菜神灯
日期:2016-04-18 13:59:20NBA季后赛大富翁
日期:2016-04-27 11:51:10NBA季后赛大富翁
日期:2016-06-24 10:29:08芝加哥公牛
日期:2015-06-25 09:32:08芝加哥公牛
日期:2016-04-18 14:22:33芝加哥公牛
日期:2016-10-27 14:28:54芝加哥公牛
日期:2016-12-27 14:16:24芝加哥公牛
日期:2017-04-18 17:07:58
30#
 楼主| 发表于 2003-11-23 13:04 | 只看该作者
数据库的查询优化技术

张在建  

数据库系统是管理信息系统的核心,基于数据库的联机事务处理(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,032???Seageat 30G disk?????……
500,049???Novel 10M network card??……
……
2.vendor表
厂商号??????厂商名??????其他列
(vendor _num)?(vendor_name) (other column)
910,257?????Seageat Corp???……
523,045?????IBM Corp?????……
……
3.parven表
零件号?????厂商号?????零件数量
(part_num)?(vendor_num)?(part_amount)
102,032????910,257????3,450,000
234,423????321,001????4,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)
part????150?????10,000????25???????400
Vendor???150?????1,000???? 25???????40
Parven???13????? 15,000????300?????? 50
索引?????键尺寸???每页键数量???页面数量
(Indexes)?(Key Size)?(Keys/Page)???(Leaf Pages)
part?????4??????500???????20
Vendor????4??????500???????2
Parven????8??????250???????60
看起来是个相对简单的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的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表