第20期:從SQL語(yǔ)法看離散性

所謂離散性,是指集合的成員可以游離在集合之外存在并參與運(yùn)算,游離成員還可以再組成新的集合。從離散性的解釋上可以知道,離散性是針對(duì)集合而言的一種能力,離開(kāi)集合概念單獨(dú)談離散性就沒(méi)有意義了。
離散性是個(gè)很簡(jiǎn)單的特性,幾乎所有支持結(jié)構(gòu)(對(duì)象)的高級(jí)語(yǔ)言都天然支持,比如我們用Java時(shí)都可以把數(shù)組成員取出來(lái)單獨(dú)計(jì)算,也可以再次組成新的數(shù)組進(jìn)行集合運(yùn)算(不過(guò)Java幾乎沒(méi)有提供集合運(yùn)算類(lèi)庫(kù))。
但是SQL的離散性卻很差。
SQL體系中有記錄的概念,但并沒(méi)有顯式的記錄數(shù)據(jù)類(lèi)型。單條記錄被SQL作為只有一條記錄的臨時(shí)表處理,也就是個(gè)單成員的集合。而且,SQL從表(集合)中取出記錄時(shí)總是復(fù)制出一條新記錄,和原表中的記錄已經(jīng)沒(méi)有關(guān)系了,這個(gè)特性被稱(chēng)為immutable。immutable特性有助于保證代碼的正確性和簡(jiǎn)單性,但也會(huì)喪失離散性。
一
缺失離散性會(huì)帶來(lái)代碼的繁瑣和效率的低下。
比如要計(jì)算張三和李四的年齡差和工資差,SQL要寫(xiě)成兩句:
- SELECT (SELECT age FROM employee WHERE name='張三') - ( SELECT age FROM employee WHERE name='李四') FROM dual
- SELECT (SELECT salary FROM employee WHERE name='張三') - ( SELECT salary FROM employee WHERE name='李四') FROM dual
這不僅書(shū)寫(xiě)麻煩,而且要重復(fù)查詢。
如果支持較好的離散性,我們可以寫(xiě)成這樣:
- a = employee.select@1(name="張三")
- b = employee.select@1(name="李四")
- aagediff=a.age-b.age
- salarydiff=a.salary-b.salary
查詢結(jié)果可以游離在集合外獨(dú)立存在,并可以反復(fù)使用。
二
immutable特性會(huì)要求每次運(yùn)算都復(fù)制數(shù)據(jù),這在只讀的運(yùn)算中還只是浪費(fèi)時(shí)間和空間影響效率,但如果要改寫(xiě)數(shù)據(jù)時(shí),造成的麻煩就嚴(yán)重得多。
比如我們想對(duì)業(yè)績(jī)?cè)谇?0%銷(xiāo)售員再給予5%的獎(jiǎng)勵(lì)。一個(gè)正常思路是先把業(yè)績(jī)?cè)谇?0%的銷(xiāo)售員找出來(lái),形成一個(gè)中間集合,然后再針對(duì)這個(gè)集合的成員執(zhí)行獎(jiǎng)勵(lì)5%的動(dòng)作。但由于SQL缺乏離散性,immutable特性導(dǎo)致滿足條件的記錄再形成的集合和原記錄是無(wú)關(guān)的,在中間結(jié)果集上做修改沒(méi)有意義。這樣就迫使我們要把整個(gè)動(dòng)作寫(xiě)成一個(gè)語(yǔ)句,直接在原表中找到滿足條件的記錄再加以修改,而前10%這種條件并不容易簡(jiǎn)單地在WHERE子句中寫(xiě)出來(lái),這又會(huì)導(dǎo)致復(fù)雜的子查詢。這還只是個(gè)簡(jiǎn)單例子,現(xiàn)實(shí)應(yīng)用中比這復(fù)雜的條件比比皆是,用子查詢也很難寫(xiě)出,經(jīng)常采用的辦法則是先把滿足條件的記錄的主鍵計(jì)算出來(lái),再用這些主鍵到原表中遍歷找到原記錄去修改,代碼繁瑣且效率極為低下。
如果語(yǔ)言支持離散性,我們就可以執(zhí)行上述思路了:
- a=sales.sort@z(amount).to(sales.len()*0.1) //取出前業(yè)績(jī)?cè)?0%的記錄構(gòu)成一個(gè)新集合
- a.run(amountamount=amount*1.05) //針對(duì)集合成員執(zhí)行獎(jiǎng)勵(lì)5%動(dòng)作
三
從上面兩個(gè)簡(jiǎn)單例子可以看出,缺失離散性會(huì)加劇分步計(jì)算的困難,immutable特性會(huì)降低性能并占用空間。當(dāng)然,離散性的問(wèn)題還不止于此。
不能用原集合的成員構(gòu)成新集合再進(jìn)行計(jì)算,SQL在做分組時(shí)無(wú)法保持分組子集,必須強(qiáng)迫聚合,作為集合化語(yǔ)言,SQL的集合化并不徹底。沒(méi)有游離記錄及其集合的表示方法,只能用傳統(tǒng)的外鍵方案表示數(shù)據(jù)之間的關(guān)聯(lián)關(guān)系,寫(xiě)出的代碼即繁瑣又難懂,而且運(yùn)算性能還差,缺乏離散性的SQL無(wú)法采用直觀的引用機(jī)制描述關(guān)聯(lián)。特別地,沒(méi)有離散性的支持,SQL很難描述有序計(jì)算,有序計(jì)算是離散性和集合化的典型結(jié)合產(chǎn)物,成員的次序在集合中才有意義,這要求集合化,有序計(jì)算時(shí)又要將每個(gè)成員與相鄰成員區(qū)分開(kāi),會(huì)強(qiáng)調(diào)離散性。
這些具體內(nèi)容我們會(huì)在后續(xù)文檔中逐步詳細(xì)說(shuō)明。我們要從理論上改進(jìn)SQL(或者更合適的說(shuō)法是關(guān)系代數(shù)),主要工作就是在保持集合化的基礎(chǔ)上引入離散性,從而解決上述問(wèn)題,讓新的語(yǔ)言能夠同時(shí)擁有SQL和Java的優(yōu)點(diǎn)。