原文鏈接:http://c.raqsoft.com.cn/article/1576562814244?r=CGQ
原文鏈接可以查看更多更詳細的sql優化示例
問題
先看數據:deliver 表是主表,一個客戶會發生多次投遞行爲:
deliverItem 表是從表,一個投遞行爲有多個投遞項,delivered 是投遞狀態(1 表示未完成,2 表示投遞完成):
需求是統計每個客戶下,全部完成的投遞行爲有幾次,未完成(只要存在未完成的投遞項,就算作未完成)的投遞行爲有幾次。
解答
自然思路的解題步驟:
1、 在deliverItem表裏統計每個投遞行爲下未完成投遞的項目數notDelivered;
2、 上一步結果和deliver表連接在一起,得到新的結果集[customer,notDelivered]
3、 按照customer分組,統計每個customer裏notDelivered=0(已完成)個數 / notDelivered>0(未完成)個數
SQL:
select r1.customer,r1.complete,r2.notComplete
from
(select customer, count(*) as complete
from
(select d.customer, d2.notDelivered
from deliver d
left join
(select deliverID,count(*) as notDelivered
from deliverItem
where delivered==1
group by deliverID) d2
on d.deliverID=d2.deliverID)
where notDelivered is null
group by customer
) r1
join
(select customer, count(*) as notComplete
from
(select d.customer, d2.notDelivered
from deliver d
left join
(select deliverID,count(*) as notDelivered
from deliverItem
where delivered==1
group by deliverID) d2
on d.deliverID=d2.deliverID)
where notDelivered <> null
group by customer
) r2
on r1.customer=r2.customer
按照開始的自然思路編寫SQL的時候,發現會遇到各種困難,通過尋找符合SQL語法的替代思路逐一解決,就得到上面的結果。需要繞行的邏輯複雜時,不同的程序員思維方式不一樣,考慮的SQL性能優化方案不同,最終利用各種技巧實現的繞行方案也會千差萬別。最終SQL的思路變成了這樣:
1、 在deliverItem表裏過濾出所有未完成的投遞項,按照deliverID分組,統計每個分組未完成項的個數netDlivered;
2、 deliver表通過左連接方式連接第一步的結果集得到新結果集[customer,notDelivered];
3、 按照customer分組,統計出每個客戶下全部完成(notDlivered=null)的投遞行爲的個數complete,得到結果集[customer,commplete];
4、 重複第1步;
5、 重複第2步;
6、 重複第3步,但稍有改動,把notDlivered=null條件變成notDlivered>0,統計出每個客戶下未完成的投遞行爲個數notComplete,得到結果集[customer, notComplete];
7、 兩個結果集連接,得到答案[customer,complete,notComplete]。
集算器SPL腳本:
A | |
1 | =connect("mysqlDB") |
2 | =A1.query(“select * from deliver”) |
3 | =A1.query(“select * from deliverItem”) |
4 | =A3.group(deliverID;~.select(delivered==1).len():notDelivered) |
5 | =A2.switch(deliverID,A4:deliverID) |
6 |
=A5.group(customer; ~.select(deliverID.notDelivered>0).len():notComlete, ~.select(deliverID.notDelivered==0).len():comlete) |
7 | =A1.close() |
A1連接數據庫;
A2/A3加載兩個表的數據(如果換成excel或csv文本等等數據,也有方便的加載函數);
A4/A5/A6是該查詢的功能語句,基本能按照自然思路完成編程;
A4把deliverItem表按deliverID分組,彙總出每個投遞行爲下未完成投遞項的個數notDelivered,包括notDelivered=0的組;
A5把A4結果集和deliver表連接起來,把deliver表的deliverID字段值用switch函數替換成A4結果集裏相對應的記錄,注意SQL表裏無法表達這種嵌套,更無法支持這種嵌套結構帶來的便捷計算操作。在下面的運行結果截圖裏能清楚的看到這種結構;
A6以customer分組,查找notDelivered>0的個數得到未完成投遞行爲個數notComplete,查找notDelivered=0的個數得到完成投遞行爲個數complete。
總結
稍微複雜點的查詢需求,寫SQL就會是個燒腦的過程,除了證明我們人腦很聰明,邏輯思維能力強之外,剩餘的就全是缺點,每個人經常用不同於其他人思路的方式繞行到同一個結果上,個性化這麼強的編程方式,導致編寫SQL、閱讀SQL、調試SQL都很困難,維護成本也大大增高。
在程序員編程描述計算這件事上,集算器 SPL 語言通過創新的數學理論模型《離散數據集》,大大改善《關係代數》(SQL背後的數學模型)在描述計算時的困難。簡單的說是對有序計算、更徹底的集合運算、提倡分步等多方面創新,達到提高程序員描述計算效率的目的。而提高描述計算效率的效果,除了降低開發、維護成本,還有個副作用是提高性能,因爲高性能算法的程序也更容易被編寫出來了。