从算法入手讲解如何在SQL Server中实现最优最简

算法是计算机科学中一个重要的研究方向,是解决复杂问题的关键。在计算机世界中,算法无处不在。数据库是存储数据和执行大批量计算的场所,在数据库中使用一些简单的SQL命令,进行存储、查询、统计、以解决现实世界中的问题已经是屡见不鲜。随着数据量的大幅度增加和业务规则的日益复杂,越来越需要一种专门的方法来满足效率和准确性方面的要求。如何把解决问题的复杂算法转换为数据库能够执行的命令,也是数据库应用技术研究的一个方面。本文以MSSQL中的命令来阐述例子。

  数据库中可以存储实体的数据集合,在进行运算时,数据库使用批量计算的方法来处理数据,批量的从存储设备上读取数据,处理之后又批量的写回存储设备。有的数据库提供了游标,游标可以读取出表中一行的数据中的每一个字段,对这些字段进行复杂的业务规则计算,然后再写回数据库中。与使用批量的方法比较,批量计算的方法消耗的资源相对比较少,而使用游标则占用太多的资源,速度比较慢,效率较低并且还有加锁条件等许多的限制。

  比如对于数据库中存储了学生成绩student_Score(sno,cno,score,level),成绩从0分到100分不等,如果需要在分数的后面存储一个字段字level来说明成绩的优劣,90分以上的A,80-90分为B,60-80分的为C,60分以下的为D,以下有几种算法都可以达到同样的目标:

  1.定义一个游标,选择student_Score表中所有的成绩记录,定义一个存储成绩的变量@cur_score,存储当前纪录的分数,定义一个存储当前分数所在成绩级别的变量@cur_level,用以存储成绩好坏的标记。算法如下:如果游标中的纪录不为空,从游标中取出当前纪录的成绩,判断成绩所在的分数段,把结果存储在变量@cur_level中,以@cur_level中的值更新当前纪录中的level字段。整个过程需要至少读取数据库两次,一次为获得纪录,一次需要写入数据库,每条记录都需要经过这个过程,效率相对低。

  2.依次批量更新数据库,把所有的level字段的值设置为D,再次更新数据库,把成绩大于等于60的纪录的Level字段更新为C,依次更新B、A。这样做的一个缺点是有些纪录的Level字段被更新多次,比如一个记录最后的Level字段的值是A,则它首先被更新为D,依次被更新为C、B、A。这些重复的更新是可以被消除的,把算法改进一下就可以省去重复更新的花费。更新后的算法是这样的,把成绩介于0和60分的纪录的Level字段更新为D,依次更新各个分数段的成绩。实现的这种算法的SQL语句并不难写出,使用Between…and…表达式即可以表达例如介于80到90之间纪录的选择条件。

  3.鉴于第二种方法最后的分析,使用between…and…表达式同时参照一个表来更新纪录,则可以方便表达分数段与相应的level信息,把这些信息存储到一个表level_about中,在更新student_score表的过程中可以参照这个表。计算的过程中,需要把level_about表的内容读出来,然后进行计算。对于整个计算过程来说,牺牲空间和部分效率来换来操作方便,,由于现在计算机的速度相当快,level_about表占用的空间又很小,这方面的损失可以忽略不记。Level_about表中的信息至少包含3个字段:start_score,记录起始分数,end_score记录终止分数,level记录介于起始分数和终止分数之间的分数应该得到的成绩。表中的数据应该类似于这样:

  Start_score End_score level

  0 59 D

  60 79 C

  80 89 B

  90 100 A

  更新student_Score表中的纪录需要依据Start_score和End_score来判断当前记录中成绩所在的Level,在MSSQL中实现的SQL语句:

  Update student_score set

  student_score.level=level_about.level from

  level_about where student.score

  between level_about.start_score and level_about.end_score

  比较以上3种方法,实现同一个目的采用不同的算法实现的效果是不同的。

一些简单的算法不需要经过修改就可以直接应用到数据库中,比如业务需要每天晚上都需要结算一天的情况,一周两次自动结算奖金,结算奖金时间在每周再周一和周四的晚上0点。为了实现系统的自动结算,需要使用系统的任务,给系统制订一个作业,指定每天晚上0点结算就可以实现系统的自动结算(由于结算的时间间隔可能是会变化,不能使用作业中的定时功能)。为了可以在周一和周四结算,在数据库中设置一个表misc,其中的字段相当于全局变量,表中只有一条纪录,使用其中的一个字段(days)来记录当前结算的次数,也就是以系统开始运行为标准经过的天数。系统执行任务同时更新misc表中的days使其增长update
misc set days=days+1。

  业务需求是每周一和周四结算奖金,不难发现奇数次结算依次相差7天,偶数次结算依次相差7天,相邻奇数次和偶数此结算相差3天,可以使用求余的方式来统一这个问题。如果当前天数(days)与7求余结果为0或者当前天数(days)减去3之后求余的结果为0,则当前天数是结算的日期。具体的实现的算法是:

  1、提取当前的天数到一个变量中declare @days int set @days=(select days from misc)。

  2、判断是否满足结算条件if @days%7=0 or (@days-3)%7=0 begin…end。

  类似于这样简单的算法可以直接的应用到数据库中而不会发生问题。

  复杂的业务规则需要复杂的算法,复杂的规则对于一个有具体数字的变量来说,实现起来已经比较复杂,如果应用到数据库中存储的杂乱无章的一大批数字,并且实现批量的计算,则需要对算法进行大幅度的调整。

  比如业务规则需要在员工每4000元的奖金中扣除400元作为重复消费,并且在扣除最后的400元,重复消费一次奖励一件产品,需要在数据库中使用一个表(award_repeat)记录产生的重复消费。如果一次扣除的奖金不足400元,在下次结算的时候接着扣除,直到扣除的奖金够400元,然后奖励一件产品,进入下次的循环,比如现在奖金总数达到了3600元,则不会扣除,如果达到了3700元,则要扣除100元,如果达到了7700元,则要扣除410元,并且产生一个重复消费。

  为了实现这个规则,在员工表(member)中记录每个员工奖金的总数([total_award]),同时记录重复消费的次数([repeat_num]),在另外的过渡表(award_day)中记录每次的奖金和每次扣除重复消费的奖金,最后在奖金表(award)中综合当次奖金和当次结算需要扣除的重复消费就得到了当次结算实际发放的奖金。采用批量的计算方法,实现的算法是:在计算奖金之后,扣除重复消费之前把当前奖金累加到员工的([total_award])字段([total_award]),记录没有扣除重复消费的所有的奖金总和。实现重复消费计算的的算法是,设定条件(F1)为在member表中存在奖金总数大于等于重复消费次数加1后乘以4000,如果有满足条件F1的记录,则选择满足条件的纪录中主键和当前的日期(days)插入到重复消费表(award_repeat)中,然后更新member表中满足条件F1的repeat_num使其增加1,重复检查条件F1,直到member表中没有满足条件F1纪录。

  结论:在数据库中研究和实现算法有着相当大的困难,同时也是一种挑战。随着现实世界中业务规则的日益复杂,相应的数据库应用软件实现业务规则需要的算法也日益复杂,把复杂的算法应用在数据库中需要找到一个统一的方式,在熟悉业务规则的前提下,根据数据库的特点和相应的执行命令的能力,找到一种适合数据库批量计算的步骤是解决问题的关键。

从算法入手讲解如何在SQL Server中实现最优最简,布布扣,bubuko.com

时间: 2024-08-05 15:20:43

从算法入手讲解如何在SQL Server中实现最优最简的相关文章

如何在 SQL Server 2005 实例之间传输登录和密码

简介 0" style="box-sizing: border-box; outline: none; margin-right: auto; margin-left: auto; max-width: 1600px; width: 761.391px;"> 本文介绍如何在不同服务器上的 Microsoft SQL Server 2005 实例之间传输登录和密码. 有关如何在其他版本的 SQL Server 实例之间传输登录和密码的更多信息,请单击下面的文章编号,以查看

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还包括:如何在SQL Server存储过程中获取另一存储过程的执行结果记录集?如何在存储过程中检索动态SQL语句的执行结果?如何实现类似SELECT * FROM (EXEC procedure_name @parameters_var) AS datasource ... 的功能?procedure_

sql server中除数为零的处理技巧

在sql server中做除法处理的时候,我们经常需要处理除数为零的情况,因为如果遇到这种情况的时候,sqlserver会抛出遇到以零作除数错误的异常,我们总不希望把这个异常显示给用户吧. 做个会报这个错误的示例,如以下sql代码: declare @a int;declare @b int;set @a=10set @b=0select @a/@b 运行就会抛出遇到以零作除数错误的错误 本章主要讲解一下如何在sql server处理除数为零的两种方法:一,利用case语句.我们改写上面的sql

SQL Server中TOP子句可能导致的问题以及解决办法

原文:SQL Server中TOP子句可能导致的问题以及解决办法 简介      在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性.      关系数据库中SQL语句只是一个抽象的概念,不包含任何逻辑.很多元数据都会影响执行计划的生成,SQL语句本身并不作为生成执行计划所参考的元数据(提示除外),但TOP关键字却是直接影响执行计划的一个关键字,因此在某些情况下使用TOP会导致性能受到影响,下面我们来

十步优化SQL Server中的数据访问(转载)

原文地址:http://tech.it168.com/a2009/1125/814/000000814758.shtml 故事开篇:你和你的团队经过不懈努力,终于使网站成功上线,刚开始时,注册用户较少,网站性能表现不错,但随着注册用户的增多,访问速度开始变慢,一些用户开始发来邮件表示抗议,事情变得越来越糟,为了留住用户,你开始着手调查访问变慢的原因. 经过紧张的调查,你发现问题出在数据库上,当应用程序尝试访问/更新数据时,数据库执行得相当慢,再次深入调查数据库后,你发现数据库表增长得很大,有些表

[转]细说SQL Server中的加密

简介 加密是指通过使用密钥或密码对数据进行模糊处理的过程.在SQL Server中,加密并不能替代其他的安全设置,比如防止未被授权的人访问数据库或是数据库实例所在的Windows系统,甚至是数据库所在的机房,而是作为当数据库被破解或是备份被窃取后的最后一道防线.通过加密,使得未被授权的人在没有密钥或密码的情况下所窃取的数据变得毫无意义.这种做法不仅仅是为了你的数据安全,有时甚至是法律所要求的(像国内某知名IT网站泄漏密码这种事在中国可以道歉后不负任何责任了事,在米国妥妥的要破产清算). SQL

为什么SQL语句Where 1=1 and在SQL Server中不影响性能

    最近一个朋友和我探讨关于Where 1=1 and这种形式的语句会不会影响性能.最后结论是不影响.     虽然结论正确,但对问题的认识却远远没有解决问题的根本.实际上在T-SQL语句的书写过程中经常犯得错误就是得出一个很窄的结论,然后教条式的奉若圣经,对于T-SQL领域来说,在网上经常可以看到所谓的优化守则,随便在网上搜了一些摘录如下: 不要有超过5个以上的表连接(JOIN) 考虑使用临时表或表变量存放中间结果 少用子查询 视图嵌套不要过深,一般视图嵌套不要超过2个为宜. 对出现在wh

理解SQL SERVER中的分区表(转)

简介 分区表是在SQL SERVER2005之后的版本引入的特性.这个特性允许把逻辑上的一个表在物理上分为很多部分.而对于SQL SERVER2005之前版本,所谓的分区表仅仅是分布式视图,也就是多个表做union操作. 分区表在逻辑上是一个表,而物理上是多个表.这意味着从用户的角度来看,分区表和普通表是一样的.这个概念可以简单如下图所示: 而对于SQL SERVER2005之前的版本,是没有分区这个概念的,所谓的分区仅仅是分布式视图: 本篇文章所讲述的分区表指的是SQL SERVER2005之

SQL Server中多表连接时驱动顺序对性能的影响

原文:SQL Server中多表连接时驱动顺序对性能的影响 本文出处:http://www.cnblogs.com/wy123/p/7106861.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他) 最近在SQL Server中多次遇到开发人员提交过来的有性能问题的SQL,其表面的原因是表之间去的驱动顺序造成的性能问题,具体表现在(已排除其他因素影响的情况下),存储过程偶发性的执行时间超出预期,甚至在调试的时候