當(dāng)面對(duì)復(fù)雜的任務(wù)時(shí),難度急劇增加,有些任務(wù)無法單獨(dú)使用SQL來實(shí)現(xiàn),因?yàn)樗皇菆D靈完備的。
在學(xué)習(xí)SQL的時(shí)候,我們經(jīng)常聽到這樣的說法:SQL是一種聲明性語言。你只需要告訴它做什么;不用告訴它怎么做,它就會(huì)找到自己的實(shí)現(xiàn)方法。也就是說,你只需要用它來描述任務(wù)目標(biāo),而不需要解釋計(jì)算過程,這與傳統(tǒng)的過程語言有本質(zhì)的區(qū)別。顯然,這種編程語言聽起來更容易學(xué)習(xí)和使用。
真的有那么好嗎?
讓我們看一個(gè)例子。我們使用SQL來查詢銷售部門的女性員工人數(shù)。這是寫好的SQL:
SELECT COUNT(*) FROM 員工表 WHERE department='Sales' AND gender='Female'
看起來是這樣的:我們不需要關(guān)心具體的計(jì)算過程(遍歷employee表中的每一條記錄,符合條件的則count加1,不符合則跳過,最后看count) ),只需說出要查詢的目標(biāo)即可。
我們?cè)倏匆粋€(gè)例子:按部門查找30歲以上員工的平均工資:
SELECT department,AVERAGE(salary) FROM 員工表 WHERE age>30 GROUP BY department
看起來也不錯(cuò)。在這里,我們真的不必關(guān)心如何分組和計(jì)算平均值。
雖然SQL仍然是一種語法嚴(yán)格的語言,但我們只有經(jīng)過一定的學(xué)習(xí)才能寫出正確的語句。不過,如果我們不關(guān)心計(jì)算過程的話,還是會(huì)省下不少功夫的。
讓我們看另一個(gè)例子。確定貢獻(xiàn)銷售額前半部分的主要客戶。如果我們?cè)O(shè)計(jì)計(jì)算過程的話,會(huì)是這樣的:
計(jì)算所有客戶的總銷售收入。
按銷售收入倒序?qū)蛻暨M(jìn)行排序,大客戶排在前,小客戶排在最后。
按此順序從0開始累加每個(gè)客戶的銷售收入,超過總銷售收入的一半時(shí)停止。那么遍歷到的客戶就是目標(biāo)客戶。
那么,用 SQL 寫出來是什么樣的呢?這是我能想到的最簡(jiǎn)單的方法:
SELECT customer, amount, cum_amount FROM ( SELECT customer,amount,SUM(amount) OVER (ORDER BY amount DESC) cum_amount FROM ordersummary ) WHERE cum_amount < (SELECT SUM(amount) FROM ordersummary)/2
仔細(xì)看看這條SQL語句,它基本上描述了上面的過程。有一個(gè)計(jì)算總銷售額的子查詢,后面跟著一個(gè)對(duì)銷售額進(jìn)行反向排序的子查詢。窗口函數(shù)用于計(jì)算排序列表中每一行的累計(jì)銷售額。然后主查詢過濾掉累計(jì)銷售額低于總銷售額一半的客戶。與上述過程唯一的區(qū)別是寫入順序。 SQL稍后開始計(jì)算總銷售收入。還有一點(diǎn)邏輯上的差異,SQL的有序計(jì)算和逐步計(jì)算不好。在找到排名靠前的產(chǎn)品之前,有必要計(jì)算所有累計(jì)銷售額。
這段SQL語句準(zhǔn)確地描述了這樣一個(gè)過程。描述任務(wù)目標(biāo)而不擔(dān)心計(jì)算過程怎么樣?
舉個(gè)更簡(jiǎn)單的例子:找到銷售收入最高的前10名客戶。
部分SQL語句寫法如下:
SELECT TOP 10 customer FROM ordersummary ORDER BY amount DESC
如果使用知名數(shù)據(jù)庫,還需要使用子查詢:
SELECT customer FROM ( SELECT rownumber rn,customer FROM ordersummary ORDER BY amount DESC ) WHERE rn<=10
這兩條SQL語句都清楚地告訴了我們計(jì)算過程:按照銷售收入反向排序后,得到前10名。而且,在這個(gè)著名數(shù)據(jù)庫的語法中,需要人為地創(chuàng)建一個(gè)序號(hào),這意味著數(shù)據(jù)庫需要更清楚地告訴如何計(jì)算。
如果我們看幾百行SQL(存儲(chǔ)過程),我們可以更清楚地看到,SQL仍然誠(chéng)實(shí)地描述了計(jì)算過程,并且不同的計(jì)算過程可以帶來截然不同的計(jì)算性能甚至結(jié)果。
事實(shí)上,任何編程語言在某種程度上都可以說是聲明性語言:即它只需要關(guān)心目標(biāo)而不需要關(guān)心過程。
如果用Java寫程序,只需要關(guān)心變量如何變化,而不用關(guān)心CPU中寄存器的動(dòng)作,但用匯編語言,就需要關(guān)心。同樣,在使用匯編語言時(shí),你需要關(guān)心寄存器的值,但不必?fù)?dān)心與非門在CPU中如何運(yùn)行。
在用SQL編寫代碼時(shí),一般不需要擔(dān)心變量和循環(huán)的具體動(dòng)作。它沒有變量的概念,但是有表、字段以及相關(guān)計(jì)算方法的概念,這個(gè)層面的流程也需要關(guān)注。從這個(gè)意義上來說,SQL和其他編程語言只是在描述問題上抽象層次不同,在解釋過程上并沒有本質(zhì)區(qū)別。
為什么很多人認(rèn)為SQL是一種所謂的聲明式語言?這是因?yàn)镾QL的設(shè)計(jì)刻意弱化了“過程性”特征,為了讓語句更像英語,將基本操作放入了語句的每個(gè)子句中。當(dāng)計(jì)算任務(wù)涉及的所有步驟都是SQL抽象層面內(nèi)的基本操作時(shí),可以將其寫成一條語句,看似向SQL描述了任務(wù)目標(biāo)。
然而,傳統(tǒng)的編程語言通常不會(huì)將多個(gè)基本操作設(shè)計(jì)成一條語句的子句或函數(shù)參數(shù),并提倡程序員將它們組合起來。這樣,人們就會(huì)覺得有必要描述這個(gè)過程,而不具有“陳述性”的特征。
然而,語句的子句結(jié)構(gòu)即使很復(fù)雜,也是有限的。當(dāng)任務(wù)超出了這個(gè)結(jié)構(gòu)的范圍,需要使用嵌套子查詢或中間結(jié)果來描述它時(shí),SQL所謂的“聲明式”假象就會(huì)暴露出來。還是有必要如實(shí)描述一下過程和方法。回顧一下前面的例子,就可以清楚地看出這一點(diǎn)。
在面對(duì)一些基本的查詢?nèi)蝿?wù)時(shí),SQL確實(shí)比Java等高級(jí)語言更容易學(xué)習(xí)和使用,但這并不是因?yàn)樗菾ava有更強(qiáng)的“聲明性”特性,而是因?yàn)樗诮Y(jié)構(gòu)化數(shù)據(jù)計(jì)算方面具有更高的抽象層次比Java。
SQL刻意弱化了“過程性”特征,比如沒有中間變量,這使得它描述過程的能力非常弱。當(dāng)面對(duì)復(fù)雜的任務(wù)時(shí),難度急劇增加,有些任務(wù)無法單獨(dú)使用SQL來實(shí)現(xiàn),因?yàn)闆]有過程化能力的SQL不是圖靈完備的。所以,數(shù)據(jù)庫廠商后來不得不補(bǔ)充和發(fā)明存儲(chǔ)過程和CTE語法。
如果我們發(fā)明一種對(duì)數(shù)據(jù)計(jì)算具有高度抽象性同時(shí)保留其過程性特征的語言,你會(huì)發(fā)現(xiàn)它比 SQL 更容易學(xué)習(xí)和使用,尤其是在面對(duì)復(fù)雜的業(yè)務(wù)邏輯時(shí)。嗯,這就是集算器SPL。文章來源:http://www.zghlxwxcb.cn/article/603.html
文章來源地址http://www.zghlxwxcb.cn/article/603.html
到此這篇關(guān)于SQL 是一種聲明式語言嗎?的文章就介紹到這了,更多相關(guān)內(nèi)容可以在右上角搜索或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!