加速SQL查詢的特徵函數法

http://www.chinaitlab.com/www/news/article_show.asp?id=1455

    1. 查詢問題的挑戰                                                  
        
    關係數據庫的查詢優化始終是一個重要而實際的問題,在那些以查詢爲主的應用系統中,
  這幾乎是一個成敗攸關的問題。但迄今爲止,關於這個問題的討論中所提出的種種解決方案大
  致可分爲兩大類,即利用硬件體系結構上的優勢及DBMS對並行處理的支持能力的一類方案及完
  全由應用設計來處理的方案。在本文作者以前所發表的文章中曾推薦過利用臨時中介表和表更
  新方法和快查詢處理的策略。在同一篇文章中,我們也曾提到有可能利用程序變換支持查詢優
  化的想法。所有這些建議和想法都屬於應用設計類的處理辦法,這些方法從某種意義上說有一
  定的一般性。但是,實際應用不斷地提出這樣或那樣難而“怪”的問題,這些問題極富挑戰性
  ,用常規方法往往要以很昂貴的系統資源爲代價纔有望解決。                            
 

    本文的目的是向讀者介紹一種由E.Birger等人首先提出的方法,即加速查詢處理的特徵函 
  數法。這個方法適用於大多數SQL的數據庫系統,如果這類 系統還包括爲數不多的幾個(最少 
  爲2個)內部函數,如abs()及sign()等,則這個方法就是直接可用的了。在E.Birger等  
  人關於這個方法的研究報告中,曾給出很多極有難度而又很典型的查詢要求及其求解辦法,其 
  中包括分技條件查詢、求行內量的邊界值、求直方圖、錶轉置、求中位值、有序集的等段截分 
  以及去邊界值問題等。這些問題的共性是,若用常規方法求解,系統無論在存儲開銷上還是處 
  理開銷上都很大,而某些問題(如中值)的求解還相當難。本文將重述這些有趣的查詢問題及 
  其解決方案。同時,我們還將討論“特徵函數”作爲一種使能技術的其他一些應用可能。     
                                                                           

    2.特徵函數及其表示                                                  
        
    特徵函數是來自點集拓撲學的一個純數學概念,集合S的特徵函數定義如下:         
    1 若x? S                                                     
           
    d s(x)= (0)                                                 
       
    0 若x? S                                                     
          

    在這裏,任意元素x是 否屬於集合S,決定函數取不同 的值。同時,這裏也隱含了一個前
  提,即任何元素的集合S爲 範圍的歸屬是完全確定的,不存在元素x的歸屬不明的情況。顯而 
  易見,特徵函數是一種識別(或判定)裝置。正是這一特性,使它能夠成爲數據庫查詢中選擇
  準則的一種等價(和更有效的)替換成分。因此,我們說特徵函數是加速查詢的實施技術。  
 

    爲了更直接地針對數據庫查詢問題,我們將特徵函數的一般形式變換成如下的“數據庫版
  本”:

    1 若a=ture                                                   
         
    d (a)= (1)                                                  
        
    0 若a=false                                                  
        

    其中α是布爾表達式。當構成布爾表達式的算術表達式由表屬性及數據庫內部函數組成時
  ,特徵函數的選擇作用就很清楚了。
 

    衆所周知,一般關係數據庫採用三值邏輯,即布爾表達式有可能取不確定值(“maybe”  
  )。但爲了簡化表達並因此突出特徵函數在加速查詢中的本質作用,本文不考慮表屬性取不確 
  定值的情形。另外,實現特徵函數的數據庫(內部)函數(我們稱之爲特徵函數的“元函數” 
  )會因系統和我們主觀選擇上的不同而不同。例如,Sybase的Transact SQL有兩個很有用的內 
  部函數abs()和sign() ,可以直接作爲特徵函數的元函數。若A和B是任意兩個表屬性,則 

    d [A!=B]=abs(sign(A-B)) (2)                       
    爲了使元函數有定義,表屬性必須是數值變量。因此,除有特別聲明而外,本文將一概假 
  定所有舉例和一般性討論中的表屬性爲非空數值變量。等式(2)可從元函數的定義          
 

    abs(A)=|A| (3)                                              
    
    -1 若A<0

    sign(A)= 0 若A=0 (4)                                       
    +1 若A>0

    直接推導出來。一般地,經abs()和sign()而實現的特徵函數是           
    d [A=B]=1-abs(sign(A-B))                             
    d [A!=B]=abs(sign(A-B)) (5)                       
    d [A

    d [A<=B]=sign(1-sign(A-B))

    d [A>B]=1-sign(1-sign(A-B))

    d [A>=B]=sign(1+sign(A-B)))

    此外,設α和b 是任意布爾表達式,則                                         
    d [NOTα]=1-d [α]                                            
  
    d [αANDb ]=d [α]*d [b ] (6)                       
    d [αOR b ]=sign(d [α]+d [b ])                   
 
    這裏的A和B是表屬性,爲非空數值量。等式(5)給出了6個最簡單的特徵函數的元函數表示,
  但這並不是唯一的表示,還可能其他的表示方法。等式(6)是布爾算子的一般導出規則。對於由
  最簡型式的關係表達式構成的布爾表達式而言,等式(5)和(6)就構成其特徵函數的實現規則。
  對於一般布爾表達式,等式(5)和(6)也是導出其特徵函數的基礎。一般而言,由(5)和(6)
  可以推演出一個特徵函數類,某些特徵函數直接對應於SQL的選擇算子。例如,形如d[A<=X<=B]
  的特徵函數顯然與判定變量X是否在閉區間[A,B]中有關。利用(5)中的第4個特徵函數及(6)
  中的第2個導出規則,

    d [A<=X<=B]

    =d [(A<=X)AND(X<=B)]

    =d [A<=X]*d [X<=B]

    =sign(1-sign(A-X
  ))*sign(1-sign(x-B)) (
  7)

    顯而易見,等式(7)右端的區則算術表達式恰是選擇算子BETWEEN的一種等價表達。可以仿照
  上述方法得到其他三個與區間值有關的特徵函數,即δ[A


    3. 實例分析                                                     
           
    爲了說明以上引入的特徵函數在加速查詢處理中的作用,讓我們具體分析一個實例。   
    試考察一個描述學生收
  selfincome)(8)
  入狀況的表 Students(name,s

  tatus,parentincome,


    其中name是主鍵,屬性status是一種標法量,當status取值1時,表明學生的收入完全來   
  自父母,當status取值0時 ,表明學生的收入完全是自己勞動所得。針對這個表,假定我們想  
  得到形如(name,income)的查詢結果,其中income或爲學生自己的收入(當相應的status取  
  0值時)或是來自父母的收 入(當相應的status取值爲1時 )。                            
 

    從表students的結構及查詢結果的語義分析,完成查詢的常規方法應當是     
    SELECT name,income=parentincome               
    FROM student                                                
      
    WHERE student=1 UNION (9)                           
    SELECT name , income=selfincome               
    FROM student                                                
      
    WHERE student=0                                             
   
    這是一個很自然、很直白的查詢表達,但同時也是一個非常低效和非常耗費資源的表達。  
  執行這個查詢的一般過程是:首先分別執行由算子UNION所連結的兩個子查詢,然後產生一個   
  存放查詢中間結果的臨時表並將兩個子查詢的結果存入以這個臨時表中,第3步對臨時表作排   
  序以便消除可能存在的重複值。至此,纔得到最終的查詢結果。在這樣的處理中,除對整個表  
  students要遍歷兩次而且要對中間結果作排序處理,處理上的煩雜和資源的消耗都是顯而易見  
  的。查詢(9)唯一的優點,是它表達上 的自然直白,誰都想得到。                          

    對本例而言,還有更緊湊和更有效的查詢表達。例如,不難驗證以下的查詢           
    SEIECT name,income=parentincome
  *status+selfincome*(1-status)

    FROM students; (10)                                       
    從語義上與查詢(9) 完全等價。但查詢(10)不但消耗的存儲少而且處理上要有效得多   
  ,因爲它只遍歷一次表students而且避免了可怕的排序操作。這個例子說明,對同一個查詢結   
  果,不同的查詢表達在處理效率和資源消耗上可能會相去甚遠。因此,尋求有效的查詢表達方   
  式,不但是必要的而且是可行的。                       
     


    查詢表達(10)與像(9)那樣的常規表達不同之處在於,後者的查詢條件由兩個WHERE
  子句和算子UNION顯式給出 ,首者將查詢條件間接地隱藏在 SELECT子句的算術表達式中。無論
  查詢表達採用什麼形式,本例都屬於“條件檢索”的查詢類 型。如果對照一下查詢要求和查詢
  表達(10),不難發現只所以能給出如此簡潔而正確的回籤 ,實在是有點“事有湊巧”。如果
  問題稍作些許改變(例如,屬性student取0和1以外的值,  或者student取兩個以上的標法值
  ,如此等等)問題就不會這麼簡單了。因此,是否有一種很 一般、很系統的解決方案,能讓我
  們對任何顯式表達在WHERE 子句和相關算子中的選擇條件找 到與之語義等價的算術表達式?答
  案是肯定的,這就是我們在下面要介紹的“特徵函數法”。  
 

    4. 幾個典型查詢的特徵函數解                                             
   
    正如上面所講,特徵函數能夠實現我們的願望,即將顯式的布爾條件轉化爲標量表達式。 
  因此,特徵函數最直接和簡便的運用是針對條件檢索型的查詢,但它的作用並不僅僅止於此。 
  爲了較全面地瞭解特徵函數在解決複雜查詢中的作用,本節將由易到難介紹和分析若干典型實 
  例。對某些實例,我們還將說明它的應用領域。爲了表達上更緊湊,所有出現在標量表達式中 
  的特徵函數都沒有用元函數展開。因此,如果要通過實際運行驗證這裏的實例,必須先借助於 
  (5)、(6)替換這裏的特徵函數。
 

    4.1 條件檢索                                                    
          
    由(10)給出的查詢可藉助於特徵函數識爲                                     
    SELECT name,                                                
      
    income=parentincome
  *d [status=1]+selfincome
  *d [status=0]

    FROM student (11)                                           

    如果檢索條件僅止於此,用(11)代替(10)並沒有什麼本質上的意義。但實際問題中的
  檢索條件遠比此處複雜而多樣。例如,若將上例的要求稍作修改,即在保留status原有語義而
  外,加上按學生的年齡分段的要求,即以19歲和23歲爲年齡的分界點,凡年齡不超過19歲而且
  依靠父母的學生爲一組,凡年齡超過23歲者完全自食其力的學生爲第二組,所有其他學生爲第
  三組。在查詢結果中,收入(income)一欄有不同的含義:對前兩組學生,分別對應於他們父
  母的收入和學生自己的收入,對第三組學生,則對應於前兩組學生收入的算術平均值。在習慣
  於用常規方法處理查詢的人看來,這樣的條件就顯得大複雜了。實際上,這是很自然的要求,
  相對於原問題而言,要求的擴展是很輕微的。對照查詢表達式(11),不難驗證            
 

    SELECT name,                                                
      
    income=parentincome*d [atatus=1]*d [age<=19]+selfincome*sign
  (d [status=O]+d [age>23])+( (parentinceome+selfinco
  me)/2.0*(1一d [status=1])*d [age<=19]-sign(d [sign=0] +d
  [age>23])

    FROM students; (12)                                       
    正是上述查詢所要求的有效表達方式。從income的表達形式看,與查詢條件的要求完全是 
  一一對應,都具有很典型的級聯式IF&#0;THEN&#0;ELSE結構。一般而言,在特徵函數的參與下 
  ,無論查詢條件多複雜(例如有更多的屬性出現在條件中,同一屬性值被劃分爲更多的區段, 
  等等),條件檢索型的查詢都具有如(11),(12)那樣的典型結構。不同之處僅僅在於,條 
  件越多則級聯數越多,但正則算術表達式的邏輯結構都相同。所有這類查詢表達,在執行中都 
  只對表student遍歷一次。相反,若按常 規方法求解,原則上每一個分類條件需要一個子查詢 
  來回答,最終的結果是所有子查詢結果往UNION運算所得。兩種表達兩種效果,孰優孰劣一目  
  瞭然。

    4. 2 直方圖問題                                                  
        

    求直方圖是統計應用中經常要解決的問題白如果統計數據來自數據庫而且數據量很大的話
  ,用常規方法求解並不是一個很輕鬆的任務。但是,借用特徵函數可以順利地解決問題,不但
  處理的過程很高效而且很直觀。爲了說明這一點,讓我們看一個具體的例子。假定統計數據存
  在表employee(name,age dept,kids)中,其中kid也表 示每個僱員的子女數。要求給出形
  如(nokids,onekids,fewkids,manykids)的統計結果,即分別計算出所有僱員中沒有孩子
  、有一個孩子的、有兩個或三個孩子的以及有三個以上孩子的僱員總數。                 

 

    如果用常規方法,需要查歷表employee四次,分別計算出nokids,onekids,fewkids和 
  manykids的值,然後經3個UNION運算才能得出最終的結果。如果原問題不是將僱員的子女數劃
  分爲4個區段而是8個甚至更多個區段,常規方法的低效就更明顯不過了。運用特徵函數,上述
  問題的解顯然是
 

    SELECT nokids=SIJM(d [kids=0]),               
    Onekids=SUM(d [kids=1])                               
    fewkids=SUM(d [2<=kids<=3])

    manykids=SUM[kids>=4])

    FROM employee; (13)                                       
    這個查詢結果的正確性很容易驗證:對於表中任意一行,如果kids=0,則d [kids=0]=1
  而且d[kids=l]=d [kids>=4]=d [2<=kids<=3]=0,所以該行在區段nokids中求和而
  不在任何其他三個區段中求和,對於kids的其他取值依此類推,這表明(13)的結果正是原問題所需
  要的結果。重要的是這個結果不但正確,得到這個結果的途境非常有效,因爲處理中只遍歷表一次。
  如果將僱員所擁有的子女數區分爲更多的值段,運用特徵函數的查詢處理仍然只遍歷表一次而不是
  更多,不同之處僅在於選擇表中的計算多幾項而已,查詢表達在邏輯上的複雜性一點也沒有增加。

    同一個基本問題也可以引導出不同的變異。若沒上述的基本解作基礎,直接解決這些變異
  問題往往很困難。


    變異問題之一:對同一個表employee,按僱員所在的不同部門分別計算子女數的分佈,即 
  要求得到如(dept,nokids,onekid,fewkids,manykids )的結果。                     

    這個問題的解顯然是以下的查詢表達                                            
  
    SELECT dept.                                                
      
    nokids=SUM(d [kids=0]),                               
    onekid=SUM(d [kids=1])                                 
    fewkids=SUM(d [2<=kids <=3]

    manykids=SUM(d [kids>=4])

    FROM employee;GROUP BY dept; (14)           
    變異問題之二:按照年齡區段求僱員子女分佈的直方圖。爲確定起見,僱員的年齡分爲三
  個區段,即:小於25歲的、大於45歲的以及年齡在25歲到45歲之間的,分別稱爲第1、第3和第
  2年齡段。這個問題事實上是要求對錶employ回給出形如(ageCategoy,nokids,onekids,  
  fewkids,manykids)的結果,其中
 
    1 若age<25

    d (a)= 2 若25<=age<=45 3 若age>45

    (15)                                                        
              
    這個問題雖有相當的難度,但對於允許表達式出現在GROUP BY子句中的系統(例如,    
  Sybase的Transact SQL就是如此),答案也是直接了當的。不難驗證以下查詢表達正是我們所
  需要的解答                                          
 
    SELECT ageCategory=1xd [age<25]+2×d [25<=age<=45] +3×d
  [age>45],

    nokids =SUM(d [kids=0]),                             
    onekids =SUM(d [kids=1]),                           
    onekid =SUM( d [kids<=3 AND Kids>=2])

    manykids =SUM(d [kids>=4])

    FROM employee                                               
     
    GROUP BY 1′ d [age<25]+2×d [25<=age<=45]+ 3×d [age>45]; (16)

    這個問題與上一個問題的區別僅僅在選擇表和GROUP BY子句中使用3年齡段表達式
  ageCaegory,按照(15)式的定義,很容易驗證(16)確是我們所需要的查詢。     
 

    沿着這個思路走下去,還可以處理更復雜的問題。當直方圖越來越“寬”時,特徵函數的
  有效性也越能顯現出來。
 

    4.3 錶轉置                                                     
           
    錶轉置是一個變換過程,它將一個窄而長的錶轉化成一種寬而短的表,這是在數據庫應用
  設計中經常遇到的問題。C .j Date很早就注意到這一點並給出了處理這一問題的一般原則。
  Date將中文中前一種形式的表稱之爲表的“列式”表示,而將後一種形式的表稱爲“行式”表
  示。鑑於SQL的集函數本質 上是面向列式表示而不是面向行式表示的,所以列式表示有便於運
  用集函數的給應用處理帶來靈活性的優點。因此,基表的設計多考慮採用列式表示一般是個好
  主意。                                             


    針對列式表示的基表作查詢時,特徵函數是實現錶轉置的有力主具。例如,考察一個記錄
  僱員月獎金的表bonus(name,month,amount)。這個表顯然是列式表示,相對於它的行式表
  示,例如就可以寫成bonus ’(name, janAmount,…,decAmount)。如果想得到每個僱員各月
  獎金一覽表,從bonus'表查詢最簡便。但這種行式表示的表本質上只對這一種查詢有效,對其
  他的查詢要求很難有適應性。相反,形如bonus的列式表示 不僅可以回答上述查詢而且還可以
  回籤其他處理要求。例如,針對上述要求的特徵函數表示就是                            


    SELECT name,                                                
      

    janAmount=SUM(amount×d [month=1]),         
    febAmount=SUM(amount×d [month=2]),         
    . . . .                                                     
           
    decAmount=SUM(amount×d [month=12])         
    FROM bonus                                                  
        
    GROUP BY name; (17)                                       
    讀者不妨思考一下,針對bonus表的同一查詢要求若不採用特徵函數該怎麼滿足。 

    4.4 求中位數                                                    
          
    在實驗數據處理中,經常有求一組數據的“中位數”(median的要求。衆所周知,關於中
  位數,存在兩種定義,即統計學定義和所謂的“財務”定義。按照統計學的定義,中位數必須
  是這組數中的某一個。因此,當有偶數個數時,必須從兩個數中作出選擇,或選大者或選小者
  ,要視具體應用背景而定。中位數的後一種定義取兩數(在有偶數個數時)的算術平均值。如
  果數值的個數爲奇數(設爲n),則中位 數就是數組中第(n+1)/2個數。不論採用什麼樣 
  的定義,用一個語句求一組數的中位數始終是一個難題。既使是訓練有素的SQL程序員,要想 
  用一般方法來處理這個問題,也得寫一個很複雜的過程。但是,借用於特徵函數,很容易得到
  這個問題的一個很簡潔的解。爲確定起見,考慮這樣的一組實驗數據data (value)。這表明 
  這組數據中不存在重複的值。除此而外,我們還要假定所有的數據都是非空的數據。在這樣的
  假定下,數據集data的中位數正是下述查詢語句的結果:                                
 

    SELECT x.value FROM data x, data y         
    GROUP BY x.value                                            
  
    HAVING SUM(d [y.value<=x.v
  alue])=(COUNT(×) +1)/2 (18)

    因爲,對於每個x.value,表達式SUM(d
  [y.value<=x.value])的結果是數據集中小於或等於該值數據的個數,所以由這個HAVING子句
  選擇的正是所要求的中位數(細心的讀者不難發現,我們這裏利用了Sybase兩個整數相除的結果
  是實際相除後再取截斷而得到的值。另外,當數據集包含偶數個元素時,取的數是兩數中較小的
  一個,這正符合中位數的統計學定義。)


    上面求中位數的方法很容易延拓到數據集往某些屬性而分割爲若干個子集的情形。例如,
  考察數據集data 2(partition,value),其中value仍爲非空數值量,但屬性partition則可
  以是任意數據類型,整個數據集data2經此屬性而分割爲若 幹個子集。下述查詢語句的結果恰
  是各個子集的中位數:                               
 

    SELECT x.partition,x.value                         
    FROM data2 x,data2 y                                     
    WHERE x. partition=y. Partition               
    GROUP BY x.Partition , x.value                 
    HAVING SUM (d [y.Value<=x.value])=(COUNT(x)+1) /2 (19)

    上式中的自連結是表分割的需要,除此而外上兩種方法沒有本質上的區別。           

    4.5 求端值                                                     
           
    在某些實際問題中,所設計的表的行數據包含了若干個可以彼此比較分析的數據項。我們
  將求這些數據項中取值最大或最小的稱爲“端值問題”。例如,考察表scores(name,sat1,
  sat2),其中sat 1和sat 2代表學生兩次考試的成績。假定需要得到每個人兩次考試中的最好
  成績的一覽表,即求形如(name,bestSat)的結果,其中bestSat表示每個學生兩次成績中的
  最好成績。                                          
 

    某些數據庫系統(例如Oracle)有內部函數greatest(value 1,value
  2…),可供直接解決問題。在不具備這種函數的系統(例如Sybase等)中,一般解決方法是,
  第一次遍歷全表得以滿足條件,sat1>=sat 2的sat 1,第二次遍歷全表得到滿足條件sat2>satl
  的sat2,再將中間結果經UNION運算才能獲得最終結果。

    藉助於特徵函數只須掃描表一遍而且查詢表達非常簡單,即:                       
    SELECT name,                                                
      
    bestSat=sat 1% d [sat 1>=sat 2]+sat 2×d [sat 2>sat1]

    FROM score; (20)                                            
  
    假定我們不只要得到每個學生兩次考試中的最好成績,而且還想知道這個成績是哪次考試
  所得,只須在(20)的選擇表中補加一項,即:         
 

    SELECT name                                                 
       
    bestSat=sat l % d [sat1>=sat 2]+sat 2% d [sat 2>sat 1]
  whichSat=1% d [sat 1>=sat 2]+2% d [sat 2>sat l]

    FROM score; (21)                                            
  
    這個結果只在sat 1=sat2 時有點歧義但並不錯(在這種情況下,(21)認爲是第一次考試 
  所得)。除此而外似乎不必再做任何解釋了。以上只考慮了最大值,求最小值可仿此辦理。有 
  興趣的讀者不妨考慮一下這個問題的種種變異情況及其解答。                             
  

    5 幾個值得進一步思考的問題                                              
     
    由(5)式給出的特徵函數表示,並不是唯一的形式, 還可有其他的表現形式。不同的元
  函數,在計算的複雜性上會有差異。因此,選擇具有更低計算複雜性的函數,有可能更進一步
  改善特徵函數的效率。其次,本文開頭關於出現在特徵函數中的屬性必須是非空數值量的假定
  ,是爲了保證元函數abs()和sign()有定義和整個(5  )式正確的充分性條件。所以,存
  在着降低這一條件強度的可能性。考慮到幾乎所有的主流數據庫系統都支持三值邏輯(3VL) 
  ,去掉非空假定就顯得特別必要了。                     


    儘管本文只考慮了特徵函數在查詢中的作用,我們認爲同樣的思想也應當針對數據庫更新
  作較系統的考察。另外,在E.birger等 人原始工作的框架內,也存在着種種明顯的可擴充的
  方面(例如,出現在特徵函數中的屬性,既可以是普通的表屬性,也應當允許是某種受限的抽
  象數據類型等等)。所有這些,都有待於我們更進一步的工作去挖掘或作出分析判斷。    
 
  下面是針對本文的測試:                                    
                                                      
  select sign(0) from ycca_xlcost                     
                                                      
  select pk_invmandoc,sum(1-sign(1-sign(endstorenum-200))) as ENDGT200,
         sum(1-sign(1-sign(endstorenum-2000))) as ENDGT2000, 
      sum(sign(1-sign(endstorenum-2000))) as ENDGT2000, 
         sum(sign(1-sign(endstorenum-200))) as ENDLQ200 from ycca_xlcost_b group by pk_invmandoc
                                                      
  select sign(1-sign(endstorenum-2000)),sum(1-sign(1-sign(endstorenum-200))) as ENDGT200,
         sum(1-sign(1-sign(endstorenum-2000))) as ENDGT200, 
      sum(sign(1-sign(endstorenum-2000))) as ENDGT200, 
         sum(sign(1-sign(endstorenum-200))) as ENDLQ200 from ycca_xlcost_b group by sign(1-sign(endstorenum-2000))
                                                      
                                                      
  select sum(sign(1)) as ddd from ycca_xlcost_b
       

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章