一句SQL完成動態分級查詢
在最近的活字格專案中使用ActiveReports報表設計器設計一個報表模板時,遇到一個多級分類的難題:需要將某個部門所有銷售及下屬部門的銷售金額彙總,因為下屬級別的層次不確定,所以靠拼接子查詢的方式顯然是不能滿足要求,經過一番實驗,利用了CTE(Common Table Expression)很輕鬆解決了這個問題!
舉例:有如下的部門表
以及員工表
如果想查詢所有西北區的員工(包含西北、西安、蘭州),如下圖所示:
如何用CTE的方式實現呢?
Talk is cheap. Show me the code
-- 以下程式碼使用SQLite 3.18.0 測試通過 WITH [depts]([dept_id]) AS( SELECT [d].[dept_id] FROM [dept] [d] JOIN [employees] [e] ON [d].[dept_id] = [e].[dept_id] WHERE [e].[emp_name] = '西北-經理' UNION ALL SELECT [d].[dept_id] FROM [dept] [d] JOIN [depts] [s] ON [d].[parent_id] = [s].[dept_id] ) SELECT * FROM [employees] WHERE [dept_id] IN (SELECT [dept_id] FROM [depts]);
可能有些同學對CTE(Common Table Expression)還不太熟悉,這裡簡單說一下,有興趣的同學可以google或者百度,介紹很多(這裡以SQLite舉例):
我還是更喜歡稱CTE(Common Table Expression)為“公用表變數”而不是“公用表示式”,因為從行為和使用場景上講,CTE更多的時候是產生(分迭代或者不迭代)結果集,供其後的語句使用(查詢、插入、刪除或更新),如上述的例子就是一個典型的利用迭代遍歷樹形結構資料。
CTE的優點:
- 遞迴的特點使得原本需要使用臨時表、儲存過程才能完成的邏輯,通過SQL就可以完成,尤其針對一些樹或者是圖的資料模型
- 因為是會話內的臨時結果集,不需要去顯示的宣告或銷燬
- 改寫後的SQL語句可讀性提高(看的明白才能修改)
- 給資料庫引擎優化執行計劃的可能性(這個不是肯定的,需要根據具體CTE的實現有關),優化了執行計劃,自然地效能就能上升
為了更好的說明CTE的能力,這裡附上兩個例子(轉自SQLite官網文件)
曼德勃羅集合(Mandelbrot set)
-- 以下程式碼使用SQLite 3.18.0 測試通過 WITH RECURSIVE xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2), yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0), m(iter, cx, cy, x, y) AS ( SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis UNION ALL SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m WHERE (x*x + y*y) < 4.0 AND iter<28 ), m2(iter, cx, cy) AS ( SELECT max(iter), cx, cy FROM m GROUP BY cx, cy ), a(t) AS ( SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '') FROM m2 GROUP BY cy ) SELECT group_concat(rtrim(t),x'0a') FROM a;
執行後的結果,如下圖:(使用SQLite Expert Personal 4.2 x64)
數獨問題(Sudoku)
假設有類似下圖的問題:
-- 以下程式碼使用SQLite 3.18.0 測試通過 WITH RECURSIVE input(sud) AS ( VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79') ), digits(z, lp) AS ( VALUES('1', 1) UNION ALL SELECT CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9 ), x(s, ind) AS ( SELECT sud, instr(sud, '.') FROM input UNION ALL SELECT substr(s, 1, ind-1) || z || substr(s, ind+1), instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' ) FROM x, digits AS z WHERE ind>0 AND NOT EXISTS ( SELECT 1 FROM digits AS lp WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1) OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1) OR z.z = substr(s, (((ind-1)/3) % 3) * 3 + ((ind-1)/27) * 27 + lp + ((lp-1) / 3) * 6, 1) ) ) SELECT s FROM x WHERE ind=0;
執行結果(結果中的數字就是對應格子中的答案)
附:SQLite中CTE(WITH關鍵字)語法圖解:
WITH
cte-table-name
Select-stmt:
總結
CTE是解決一些特定問題的利器,但瞭解和正確的使用是前提,在決定將已有的一些SQL重構為CTE之前,確保對已有語句有清晰的理解以及對CTE足夠的學習!Good Luck~~~
附件:用到的SQL指令碼
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28298702/viewspace-2137869/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PHP--動態生成sql查詢表格PHPSQL
- 如何完成複雜查詢的動態構建?
- 如何使用PL/SQL進行分級查詢WPSQL
- Spring Data Jpa 的簡單查詢多表查詢HQL,SQL ,動態查詢, QueryDsl ,自定義查詢筆記SpringSQL筆記
- Oracle總結【SQL細節、多表查詢、分組查詢、分頁】OracleSQL
- MyBatis(六) sql片段定義、級聯查詢、巢狀查詢MyBatisSQL巢狀
- PB帶引數帶結果集的動態SQL查詢SQL
- Mybatis連線池_動態sql語句_多表查詢實現MyBatisSQL
- jpa動態查詢與多表聯合查詢
- 千萬級資料深分頁查詢SQL效能最佳化實踐SQL
- 一條SQL完成跨資料庫例項Join查詢SQL資料庫
- Mybatis 裡對映檔案的動態 SQL 語句,實現if,where,foreache的SQL語句動態拼接查詢MyBatisSQL
- Specifications 構建動態查詢
- mybatis動態sql與分頁MyBatisSQL
- SQL查詢的:子查詢和多表查詢SQL
- 秒級查詢之開源分散式SQL查詢引擎Presto實操-上分散式SQLREST
- Springboot中配置動態sql查詢出現的錯誤syntax error, expect ‘)‘Spring BootSQLError
- 原生SQL查詢SQL
- SQL--查詢SQL
- SQL 聚合查詢SQL
- 查詢統計SQL分組求和使用小技巧SQL
- SpringDataJpa (二)-動態查詢&多表操作Spring
- 查詢——二分查詢
- 動態SQL-條件分頁SQL
- 百億級資料分表後怎麼分頁查詢?
- 使用SSH完成條件及分頁查詢的主要程式碼
- 百億級資料 分庫分表 後怎麼分頁查詢?
- SQL查詢總結SQL
- SQL連線查詢SQL
- SQL高階查詢SQL
- sql常用查詢命令SQL
- SQL 複雜查詢SQL
- ElasticSearch - 分頁查詢方式二 【scroll】滾動查詢(kibana、Java示例)ElasticsearchJava
- django 動態查詢實現過程Django
- HighgoDB查詢慢SQL和阻塞SQLGoSQL
- pid,sid相互查詢,根據PID查詢sqlSQL
- 效率升級,從NAS開始:儲存、查詢、共享一鍵完成
- 億萬級分庫分表後如何進行跨表分頁查詢