時(shí)間:2015-06-28 00:00:00 來源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評論(0)
算法是計(jì)算機(jī)科學(xué)中一個(gè)重要的研究方向,是解決復(fù)雜問題的關(guān)鍵。在計(jì)算機(jī)世界中,算法無處不在。數(shù)據(jù)庫是存儲數(shù)據(jù)和執(zhí)行大批量計(jì)算的場所,在數(shù)據(jù)庫中使用一些簡單的SQL命令,進(jìn)行存儲、查詢、統(tǒng)計(jì)、以解決現(xiàn)實(shí)世界中的問題已經(jīng)是屢見不鮮。隨著數(shù)據(jù)量的大幅度增加和業(yè)務(wù)規(guī)則的日益復(fù)雜,越來越需要一種專門的方法來滿足效率和準(zhǔn)確性方面的要求。如何把解決問題的復(fù)雜算法轉(zhuǎn)換為數(shù)據(jù)庫能夠執(zhí)行的命令,也是數(shù)據(jù)庫應(yīng)用技術(shù)研究的一個(gè)方面。本文以MSSQL中的命令來闡述例子。
數(shù)據(jù)庫中可以存儲實(shí)體的數(shù)據(jù)集合,在進(jìn)行運(yùn)算時(shí),數(shù)據(jù)庫使用批量計(jì)算的方法來處理數(shù)據(jù),批量的從存儲設(shè)備上讀取數(shù)據(jù),處理之后又批量的寫回存儲設(shè)備。有的數(shù)據(jù)庫提供了游標(biāo),游標(biāo)可以讀取出表中一行的數(shù)據(jù)中的每一個(gè)字段,對這些字段進(jìn)行復(fù)雜的業(yè)務(wù)規(guī)則計(jì)算,然后再寫回?cái)?shù)據(jù)庫中。與使用批量的方法比較,批量計(jì)算的方法消耗的資源相對比較少,而使用游標(biāo)則占用太多的資源,速度比較慢,效率較低并且還有加鎖條件等許多的限制。
比如對于數(shù)據(jù)庫中存儲了學(xué)生成績student_Score(sno,cno,score,level),成績從0分到100分不等,如果需要在分?jǐn)?shù)的后面存儲一個(gè)字段字level來說明成績的優(yōu)劣,90分以上的A,80-90分為B,60-80分的為C,60分以下的為D,以下有幾種算法都可以達(dá)到同樣的目標(biāo):
1.定義一個(gè)游標(biāo),選擇student_Score表中所有的成績記錄,定義一個(gè)存儲成績的變量@cur_score,存儲當(dāng)前紀(jì)錄的分?jǐn)?shù),定義一個(gè)存儲當(dāng)前分?jǐn)?shù)所在成績級別的變量@cur_level,用以存儲成績好壞的標(biāo)記。算法如下:如果游標(biāo)中的紀(jì)錄不為空,從游標(biāo)中取出當(dāng)前紀(jì)錄的成績,判斷成績所在的分?jǐn)?shù)段,把結(jié)果存儲在變量@cur_level中,以@cur_level中的值更新當(dāng)前紀(jì)錄中的level字段。整個(gè)過程需要至少讀取數(shù)據(jù)庫兩次,一次為獲得紀(jì)錄,一次需要寫入數(shù)據(jù)庫,每條記錄都需要經(jīng)過這個(gè)過程,效率相對低。
2.依次批量更新數(shù)據(jù)庫,把所有的level字段的值設(shè)置為D,再次更新數(shù)據(jù)庫,把成績大于等于60的紀(jì)錄的Level字段更新為C,依次更新B、A。這樣做的一個(gè)缺點(diǎn)是有些紀(jì)錄的Level字段被更新多次,比如一個(gè)記錄最后的Level字段的值是A,則它首先被更新為D,依次被更新為C、B、A。這些重復(fù)的更新是可以被消除的,把算法改進(jìn)一下就可以省去重復(fù)更新的花費(fèi)。更新后的算法是這樣的,把成績介于0和60分的紀(jì)錄的Level字段更新為D,依次更新各個(gè)分?jǐn)?shù)段的成績。實(shí)現(xiàn)的這種算法的SQL語句并不難寫出,使用Between…and…表達(dá)式即可以表達(dá)例如介于80到90之間紀(jì)錄的選擇條件。
3.鑒于第二種方法最后的分析,使用between…and…表達(dá)式同時(shí)參照一個(gè)表來更新紀(jì)錄,則可以方便表達(dá)分?jǐn)?shù)段與相應(yīng)的level信息,把這些信息存儲到一個(gè)表level_about中,在更新student_score表的過程中可以參照這個(gè)表。計(jì)算的過程中,需要把level_about表的內(nèi)容讀出來,然后進(jìn)行計(jì)算。對于整個(gè)計(jì)算過程來說,犧牲空間和部分效率來換來操作方便,,由于現(xiàn)在計(jì)算機(jī)的速度相當(dāng)快,level_about表占用的空間又很小,這方面的損失可以忽略不記。Level_about表中的信息至少包含3個(gè)字段:start_score,記錄起始分?jǐn)?shù),end_score記錄終止分?jǐn)?shù),level記錄介于起始分?jǐn)?shù)和終止分?jǐn)?shù)之間的分?jǐn)?shù)應(yīng)該得到的成績。表中的數(shù)據(jù)應(yīng)該類似于這樣:
Start_score End_score level
0 59 D
60 79 C
80 89 B
90 100 A
?
更新student_Score表中的紀(jì)錄需要依據(jù)Start_score和End_score來判斷當(dāng)前記錄中成績所在的Level,在MSSQL中實(shí)現(xiàn)的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種方法,實(shí)現(xiàn)同一個(gè)目的采用不同的算法實(shí)現(xiàn)的效果是不同的。
一些簡單的算法不需要經(jīng)過修改就可以直接應(yīng)用到數(shù)據(jù)庫中,比如業(yè)務(wù)需要每天晚上都需要結(jié)算一天的情況,一周兩次自動結(jié)算獎金,結(jié)算獎金時(shí)間在每周再周一和周四的晚上0點(diǎn)。為了實(shí)現(xiàn)系統(tǒng)的自動結(jié)算,需要使用系統(tǒng)的任務(wù),給系統(tǒng)制訂一個(gè)作業(yè),指定每天晚上0點(diǎn)結(jié)算就可以實(shí)現(xiàn)系統(tǒng)的自動結(jié)算(由于結(jié)算的時(shí)間間隔可能是會變化,不能使用作業(yè)中的定時(shí)功能)。為了可以在周一和周四結(jié)算,在數(shù)據(jù)庫中設(shè)置一個(gè)表misc,其中的字段相當(dāng)于全局變量,表中只有一條紀(jì)錄,使用其中的一個(gè)字段(days)來記錄當(dāng)前結(jié)算的次數(shù),也就是以系統(tǒng)開始運(yùn)行為標(biāo)準(zhǔn)經(jīng)過的天數(shù)。系統(tǒng)執(zhí)行任務(wù)同時(shí)更新misc表中的days使其增長update misc set days=days+1。
業(yè)務(wù)需求是每周一和周四結(jié)算獎金,不難發(fā)現(xiàn)奇數(shù)次結(jié)算依次相差7天,偶數(shù)次結(jié)算依次相差7天,相鄰奇數(shù)次和偶數(shù)此結(jié)算相差3天,可以使用求余的方式來統(tǒng)一這個(gè)問題。如果當(dāng)前天數(shù)(days)與7求余結(jié)果為0或者當(dāng)前天數(shù)(days)減去3之后求余的結(jié)果為0,則當(dāng)前天數(shù)是結(jié)算的日期。具體的實(shí)現(xiàn)的算法是:
1、提取當(dāng)前的天數(shù)到一個(gè)變量中declare @days int set @days=(select days from misc)
2、判斷是否滿足結(jié)算條件if @days%7=0 or (@days-3)%7=0 begin…end
類似于這樣簡單的算法可以直接的應(yīng)用到數(shù)據(jù)庫中而不會發(fā)生問題。
復(fù)雜的業(yè)務(wù)規(guī)則需要復(fù)雜的算法,復(fù)雜的規(guī)則對于一個(gè)有具體數(shù)字的變量來說,實(shí)現(xiàn)起來已經(jīng)比較復(fù)雜,如果應(yīng)用到數(shù)據(jù)庫中存儲的雜亂無章的一大批數(shù)字,并且實(shí)現(xiàn)批量的計(jì)算,則需要對算法進(jìn)行大幅度的調(diào)整。
比如業(yè)務(wù)規(guī)則需要在員工每4000元的獎金中扣除400元作為重復(fù)消費(fèi),并且在扣除最后的400元,重復(fù)消費(fèi)一次獎勵(lì)一件產(chǎn)品,需要在數(shù)據(jù)庫中使用一個(gè)表(award_repeat)記錄產(chǎn)生的重復(fù)消費(fèi)。如果一次扣除的獎金不足400元,在下次結(jié)算的時(shí)候接著扣除,直到扣除的獎金夠400元,然后獎勵(lì)一件產(chǎn)品,進(jìn)入下次的循環(huán),比如現(xiàn)在獎金總數(shù)達(dá)到了3600元,則不會扣除,如果達(dá)到了3700元,則要扣除100元,如果達(dá)到了7700元,則要扣除410元,并且產(chǎn)生一個(gè)重復(fù)消費(fèi)。
為了實(shí)現(xiàn)這個(gè)規(guī)則,在員工表(member)中記錄每個(gè)員工獎金的總數(shù)([total_award]),同時(shí)記錄重復(fù)消費(fèi)的次數(shù)([repeat_num]),在另外的過渡表(award_day)中記錄每次的獎金和每次扣除重復(fù)消費(fèi)的獎金,最后在獎金表(award)中綜合當(dāng)次獎金和當(dāng)次結(jié)算需要扣除的重復(fù)消費(fèi)就得到了當(dāng)次結(jié)算實(shí)際發(fā)放的獎金。采用批量的計(jì)算方法,實(shí)現(xiàn)的算法是:在計(jì)算獎金之后,扣除重復(fù)消費(fèi)之前把當(dāng)前獎金累加到員工的([total_award])字段([total_award]),記錄沒有扣除重復(fù)消費(fèi)的所有的獎金總和。實(shí)現(xiàn)重復(fù)消費(fèi)計(jì)算的的算法是,設(shè)定條件(F1)為在member表中存在獎金總數(shù)大于等于重復(fù)消費(fèi)次數(shù)加1后乘以4000,如果有滿足條件F1的記錄,則選擇滿足條件的紀(jì)錄中主鍵和當(dāng)前的日期(days)插入到重復(fù)消費(fèi)表(award_repeat)中,然后更新member表中滿足條件F1的repeat_num使其增加1,重復(fù)檢查條件F1,直到member表中沒有滿足條件F1紀(jì)錄。
結(jié)論:在數(shù)據(jù)庫中研究和實(shí)現(xiàn)算法有著相當(dāng)大的困難,同時(shí)也是一種挑戰(zhàn)。隨著現(xiàn)實(shí)世界中業(yè)務(wù)規(guī)則的日益復(fù)雜,相應(yīng)的數(shù)據(jù)庫應(yīng)用軟件實(shí)現(xiàn)業(yè)務(wù)規(guī)則需要的算法也日益復(fù)雜,把復(fù)雜的算法應(yīng)用在數(shù)據(jù)庫中需要找到一個(gè)統(tǒng)一的方式,在熟悉業(yè)務(wù)規(guī)則的前提下,根據(jù)數(shù)據(jù)庫的特點(diǎn)和相應(yīng)的執(zhí)行命令的能力,找到一種適合數(shù)據(jù)庫批量計(jì)算的步驟是解決問題的關(guān)鍵。
關(guān)鍵詞標(biāo)簽:SQL數(shù)據(jù)庫
相關(guān)閱讀
熱門文章 淺談JSP JDBC來連接SQL Server 2005的方法 SqlServer2005對現(xiàn)有數(shù)據(jù)進(jìn)行分區(qū)具體步驟 sql server系統(tǒng)表損壞的解決方法 MS-SQL2005服務(wù)器登錄名、角色、數(shù)據(jù)庫用戶、角色、架構(gòu)的關(guān)系
人氣排行 配置和注冊O(shè)DBC數(shù)據(jù)源-odbc數(shù)據(jù)源配置教程 如何遠(yuǎn)程備份(還原)SQL2000數(shù)據(jù)庫 SQL2000數(shù)據(jù)庫遠(yuǎn)程導(dǎo)入(導(dǎo)出)數(shù)據(jù) SQL2000和SQL2005數(shù)據(jù)庫服務(wù)端口查看或修改 修改Sql Server唯一約束教程 SQL Server 2005降級到2000的正確操作步驟 sql server系統(tǒng)表損壞的解決方法 淺談JSP JDBC來連接SQL Server 2005的方法