從 SQL 到集算器的基本查詢語法遷移(一)單表操作
創(chuàng)新互聯(lián)"三網(wǎng)合一"的企業(yè)建站思路。企業(yè)可建設(shè)擁有電腦版、微信版、手機版的企業(yè)網(wǎng)站。實現(xiàn)跨屏營銷,產(chǎn)品發(fā)布一步更新,電腦網(wǎng)絡(luò)+移動網(wǎng)絡(luò)一網(wǎng)打盡,滿足企業(yè)的營銷需求!創(chuàng)新互聯(lián)具備承接各種類型的網(wǎng)站建設(shè)、成都網(wǎng)站制作項目的能力。經(jīng)過10年的努力的開拓,為不同行業(yè)的企事業(yè)單位提供了優(yōu)質(zhì)的服務(wù),并獲得了客戶的一致好評。
數(shù)據(jù)庫和數(shù)據(jù)分析領(lǐng)域,有一個強大的存在,大名 SQL,全名結(jié)構(gòu)化查詢語言 (Structured Query Language)。從字面上看,這個語言的目標(biāo)就是把數(shù)據(jù)“查詢”出來,而查詢這個詞給人的感覺并不是很難。但實際上,為了支持貌似簡單的查詢,需要進行大量的計算動作,甚至整個過程就是一個多步驟的計算,前后步驟之間還有很強的依賴關(guān)系,前面計算的結(jié)果要被后面使用,而后面的輸出有可能需要我們對前面的計算進行調(diào)整。
打個比方,這有點類似于去各類網(wǎng)點辦事,填表遞交后,相關(guān)辦事人員開始在窗口后忙忙碌碌,時不時還會甩回來幾個問題要求澄清,等到最后拿到回復(fù),還有可能根本不是自己期望的結(jié)果!這時候,坐在辦事窗口外的我們,抓狂之余,往往會產(chǎn)生一個念頭,如果我們能夠看到,甚至參與到過程中,應(yīng)該能夠大大地提高辦事效率。
沒錯,你應(yīng)該能想到,下面要介紹的集算器,和 SQL 相比對于我們這些過程控來說,就是一個可以輕松把玩和控制的計算(不止是查詢)工具。
我們要做的,就是“照貓畫虎”地把習(xí)慣中的 SQL 操作遷移到集算器中,用小小的習(xí)慣改變,換來大大的效益提升。
首先,我們需要把數(shù)據(jù)從傳統(tǒng)的數(shù)據(jù)源中“搬遷”到集算器中,這樣后續(xù)的操作就可以完全在集算器中進行了。
我們最常用的數(shù)據(jù)源一般就是關(guān)系數(shù)據(jù)庫 RDB。這里使用的樣例數(shù)據(jù),就是數(shù)據(jù)庫中的兩個數(shù)據(jù)表:
訂單信息表(order,主鍵 orderId),包括訂單編號orderId、客戶代碼customerId、雇員編號employeeId、訂單日期orderDate、發(fā)送日期sendDate以及金額money****:
orderId | customerId | employeeId | orderDate | sendDate | money |
---|---|---|---|---|---|
10248 | VINET | 2 | 2011-02-04 | 2011-02-16 | 440 |
10249 | TOMSP | 9 | 2011-02-05 | 2011-02-10 | 1863 |
10250 | HANAR | 9 | 2011-02-08 | 2011-02-12 | 1813 |
10251 | VICTE | 9 | 2011-02-08 | 2011-02-15 | 670 |
訂單明細表(orderDetail,主鍵 orderId,productId),包括訂單編號orderId、產(chǎn)品編號productId、價格price、數(shù)量amount、折扣discount:
orderId | productId | price | amount | discount |
---|---|---|---|---|
11059 | 17 | 39 | 12 | 0.85 |
11059 | 60 | 34 | 35 | 0.9 |
11060 | 60 | 34 | 4 | 1 |
11060 | 77 | 13 | 10 | 0.95 |
“搬遷”,或者說導(dǎo)入數(shù)據(jù)的過程非常簡單,如下圖所示:
集算器 | A |
---|---|
1 | =connect(“hsqlDB”) |
2 | =A1.query(“select * from order”) |
3 | >A1.close() |
首先建立數(shù)據(jù)庫連接(網(wǎng)格 A1),然后直接通過單表全量查詢的 SQL 語句從數(shù)據(jù)庫中讀取數(shù)據(jù)(網(wǎng)格 A2),最后清理現(xiàn)場,關(guān)閉數(shù)據(jù)庫連接(網(wǎng)格 A3)。
在執(zhí)行了腳本后,我們可以選中網(wǎng)格 A2,在結(jié)果區(qū)中看看搬過來的數(shù)據(jù),同時,order 表在集算器中也換了個身份,我們稱之為“序表”,用網(wǎng)格名稱 A2 代表。序表是集算器中一個非常重要的概念,現(xiàn)在我們可以簡單地把它理解成對應(yīng)數(shù)據(jù)庫中的一張表:
其實,在集算器中,任何一個有計算結(jié)果的網(wǎng)格(一般是用等號“=”開始),都可以在執(zhí)行腳本后,隨時選中并查看結(jié)果,并通過網(wǎng)格名稱 A7、B8 等隨時引用,從而滿足我們隨時監(jiān)控的欲望……
接下來,我們以 SQL 中 select 語句的各個子句為線索,看看集算器中是如何操作的:
用來選擇需要查詢的字段,也可以通過表達式來對字段進行計算,或者重命名字段。與之對應(yīng)的,集算器里有 new、derive、run 三個函數(shù)。
例如:只選出訂單表里的訂單編號、雇員編號、訂單日期以及金額字段,同時把金額乘以 100 后使它的單位從元變成分,把相應(yīng)的結(jié)果字段重命名為 centMoney。
SQL 的做法如下:
SQL |
---|
SELECT orderId,employeeId,orderDate,money*100 centMoney FROM order |
集算器對應(yīng)的做法是下表中的 A3:
集算器 | A |
---|---|
1 | =connect(“hsqlDB”) |
2 | =A1.query(“SELECT * FROM order”) |
3 | =A2.new(orderId,employeeId,orderDate,money*100:centMoney) |
4 | =A3.derive(year(orderDate):orderYear) |
5 | =A4.run(string(orderYear)+”年”:orderYear) |
6 | >A1.close() |
A3 利用 A2 的數(shù)據(jù)新建了一個序表,包含了要求的字段,包括把金額乘以 100 后用 centMoney 命名:
我們繼續(xù)看下 A4 的結(jié)果,在序表 A3 的原有字段后增加了新字段 orderYear,這也就是說 derive(新計算字段) 相當(dāng)于 new(所有老字段, 新計算字段),可以看做是 new 函數(shù)的一種簡單的寫法,免去了把老字段再抄寫一遍。
A5 使用了 run 函數(shù),直接作用是修改老字段 orderYear 的值,但是再看 A4 的結(jié)果,也變成和 A5 一樣了。這是因為 run 函數(shù)并沒有像 new、derive 函數(shù)那樣生成新的序表對象,而是在原來對象上直接做修改。
總結(jié)一下,在集算器中,new、derive、run 函數(shù)都會產(chǎn)生序表結(jié)果,但是 new、derive 函數(shù)會生成一個新的序表對象,像是把數(shù)據(jù)復(fù)制出來(這個行為有個專有名詞immutable),而 run 則直接修改被處理對象(行為屬于mutable)。
【延伸閱讀】之所以提出mutable這樣的行為,有兩個因素:首先是減少內(nèi)存占用,從而提高性能;其次,有些實際業(yè)務(wù)的需求就需要改變原有對象,一系列的動作直接作用在一個對象上,到最后一步就得到正確結(jié)果了,而不是得到一堆中間結(jié)果,再做最后合并的動作。當(dāng)然 immutable 也有它的適用場景,兩種行為本身沒有優(yōu)劣之分。
用來對數(shù)據(jù)表按條件進行過濾。與之對應(yīng)的,集算器通過 select 函數(shù)對一個序表的記錄進行過濾。效果如下圖所示:
針對前面的示例數(shù)據(jù),我們希望查詢指定時段(2012 年 1 月期間)的訂單數(shù)據(jù),可以對比一下 SQL 和集算器(網(wǎng)格 A3)的做法。
SQL |
---|
SELECT * FROM order |
WHERE orderDate>=’2012-01-01′ AND orderDate<‘2012-02-01’ |
集算器 | A |
---|---|
1 | =connect(“hsqlDB”) |
2 | =A1.query(“SELECT * FROM order”) |
3 | =A2.select(orderDate>=date(“2012-01-01”) && orderDate |
4 | >A1.close() |
需要注意一下集算器表達式中有兩個細節(jié):一是用了 date 函數(shù)把字符串轉(zhuǎn)換成日期類型,二是 AND/OR 在集算器里的寫法是 &&/||。
A3 的結(jié)果如下:
看起來和 A2 結(jié)構(gòu)一致,只是數(shù)據(jù)量小了。但是我們可以做一個實驗,在網(wǎng)格 B3 中輸入“=A2.select(orderId=10555).run(money*10:money)”,修改 A2 序表中某些記錄的字段值,可以看到 A3 序表里這些對應(yīng)記錄的值也跟著變化了。這就說明兩個序表里的記錄就是同一個東西(叫做對象會顯得更專業(yè)點),也就是說集算器里的 select 函數(shù)屬于我們前面所說的 mutable 行為。
GROUPY BY 經(jīng)常和聚合函數(shù) SUM、COUNT 等一起出現(xiàn),用來將查詢結(jié)果按照某些字段進行歸類分組,并匯總統(tǒng)計。嚴(yán)格來說,這是兩個獨立的動作,但在 SQL 中總是一起出現(xiàn),從而給人一種兩者必須同時使用的假象。事實上,這種組合是對分組操作的一種局限,或者說分組之后,能夠進行的計算遠不止 SQL 中的幾種聚合函數(shù)。在集算器中,與 GROUP BY 子句對應(yīng)的是 group 函數(shù),它可以只對數(shù)據(jù)純粹分組,每組形成一個小集合,在后面的計算中可以針對這些小集合進行簡單的聚合,也可以進行更加復(fù)雜的計算。下圖是 SQL 中利用 GROUP BY 進行分組求和的示意:
同樣還是前面的示例數(shù)據(jù),我們希望計算 2012 年 1 月期間每個雇員的銷售總額,也就是按照雇員編號分組后求和。針對這個分組求和的計算,我們對比一下 SQL 和集算器的做法:
SQL |
---|
SELECT employeeId, sum(money) salesAmount FROM order WHERE orderDate>=’2012-01-01′ AND orderDate<‘2012-02-01’ GROUP BY employeeId |
集算器 | A |
---|---|
1 | =connect(“hsqlDB”) |
2 | =A1.query(“SELECT * FROM order”) |
3 | =A2.select(orderDate>=date(“2012-01-01”) && orderDate |
4 | =A3.group(employeeId;~.sum(money):salesAmount) |
5 | >A1.close() |
A4 的結(jié)果如下:
集算器把查詢分解成了三步:
首先,是 A2 取出訂單表中的所有記錄;
然后,A3 過濾得到指定時段(2012 年 1 月期間)的訂單記錄
最后,A4 把過濾得到的記錄按照雇員編號進行分組(由函數(shù)參數(shù)中分號前的部分定義,可以同時指定多個字段),同時對每個分組(用“~”符號表示)進行求和(sum)計算,并把計算結(jié)果的字段命名為 salesAmount(分號后的部分)
看起來和 SQL 分組沒什么不用,這只是因為我們這個例子只演示了和 SQL 相同的分組查詢。實際上 A4 里 group 函數(shù)的后半部分不是必須的,也可能有的業(yè)務(wù)僅僅是想得到分組后的結(jié)果,而不在這里進行求和、計數(shù)運算;也可能針對特定值的分組有不同的聚合運算,那就針對分組后的小集合“~”寫更復(fù)雜的表達式就可以了。
同時,在其他教程中,我們還可以了解到,分組字段不局限于原始字段,可以是一個表達式,這個和 SQL 里一樣。
單純的分組屬于mutable行為,是把一個大集合的記錄拆分到多個小集合里。而加上聚合運算后,因為產(chǎn)生了新的數(shù)據(jù)結(jié)構(gòu),就不再是簡單的把原有記錄挪挪地方的操作了。
用來對 GROUP BY 后的聚合結(jié)果再進行過濾。在集算器里,就沒有專門對應(yīng) HAVING 的函數(shù)了,對任何序表按照條件過濾都用 select 函數(shù),因為計算是分步的,操作指向的對象總是明確的。而 SQL 要求一句話把所有事情表達出來,遇到復(fù)雜查詢,就難免出現(xiàn)對同一性質(zhì)的操作增加概念,以表達作用到不同的對象上。再深想一下,HAVING 概念在 SQL 里也不是必須的,它是對第一層子查詢的簡化寫法:
SELECT f1, sum(f2) f2 FROM t GROUP BY f1 HAVING sum(f2)>100
等價于
SELECT * FROM
(SELECT f1, sum(f2) f2sum FROM t GROUP BY f1) t2
WHERE f2sum >100
對更多層子查詢做同類簡化,估計會出現(xiàn) HAVING2、HAVING3…類似的關(guān)鍵字,但 HAVING2 之后的簡化性價比不高,SQL 也就沒有提供了。這里又體現(xiàn)出分步計算的一個優(yōu)勢,只需要描述計算本質(zhì)需要的概念,HAVING、子查詢這些因為技術(shù)手段被迫產(chǎn)生的概念就可以棄用了。減少非必要概念是降低學(xué)習(xí)成本的重要手段。
我們具體看一下 SQL 和集算器的做法的對比,找到 2012 年 1 月期間銷售額超過 5000 的雇員編號和他的銷售總額:
SQL |
---|
SELECT employeeId, SUM(money) salesAmount FROM order WHERE orderDate>=’2012-01-01′ AND orderDate<‘2012-02-01’ GROUP BY employeeId HAVING SUM(money)>5000 |
集算器 | A |
---|---|
1 | =connect(“hsqlDB”) |
2 | =A1.query(“SELECT * FROM order”) |
3 | =A2.select(orderDate>=date(“2012-01-01”) && orderDate |
4 | =A3.group(employeeId;~.sum(money):salesAmount) |
5 | =A4.select(salesAmount>5000) |
6 | >A1.close() |
A5 結(jié)果
隨著查詢復(fù)雜度逐步提升,集算器語句容易閱讀,步驟流程清晰的特點也就凸顯出來了。每一步都可以觀察結(jié)果,根據(jù)結(jié)果隨意控制計算流程,用最精簡的概念描述每個計算步驟。這還只是一個最簡單的單表查詢例子,下一篇我們會繼續(xù)了解在多表連接和聯(lián)合的情況下,集算器會有怎樣更加優(yōu)秀的表現(xiàn)。