如何將模組化應用於 SQL
在本文中,我們將瞭解模組化這一最重要的系統設計原則之一如何應用於 SQL。
定義: 模組是一個單元,其元素與自身緊密相連,但與其他單元弱相連。
當系統在設計時考慮到模組化,獨立方很容易並行構建這些元件,以便以後組裝。它還使得在生產中除錯和修復系統變得容易。
模組化是作業系統設計的核心原則之一。如果您熟悉 Mac 上的命令列介面,您就會看到它的強大功能。它的設計方式是將獨立的工具拼接在一起,以解決越來越複雜的問題。
在本文中,我們將學習如何將其應用於 SQL。
三個層次的模組化
在 SQL 中,我們可以在 3 個不同級別應用模組化:
- 在同一個 SQL 查詢中
- 跨多個 SQL 查詢
- 超越 SQL 查詢
您是否曾經編寫或除錯過很長的 SQL 查詢?您是否在試圖弄清楚它在做什麼時迷失了方向,或者它真的很容易理解嗎?
您是否迷路在很大程度上取決於查詢是否使用 CTE 將問題分解為邏輯模組,從而使解決問題和理解問題變得非常容易。
級別 1 - 在同一個 SQL 查詢中
CTE 或公用表表示式是臨時檢視,其範圍僅限於當前查詢。它們不儲存在資料庫中;它們僅在查詢執行時存在,並且只能在該查詢中訪問。它們的行為類似於子查詢,但更易於理解和使用。
CTE 允許您將複雜的查詢分解為更簡單、更小的獨立模組。透過將它們連線在一起,我們可以解決任何複雜的查詢。
旁註: 儘管 CTE 自 1999 年以來一直是 SQL 標準定義的一部分,但資料庫供應商花了很多年才實現它們。某些版本的舊資料庫(如 8.0 之前的 MySQL、8.4 之前的 PostgreSQL、2005 之前的 SQL Server)不支援 CTE。所有現代雲倉庫供應商都支援它們。
視覺化 CTE 的最佳方法之一是透過 DAG(有向迴圈圖)。
以下是如何連結 CTE 以解決複雜查詢的一些示例。
這是第一張圖及其相應的程式碼。
在此示例中,每個 CTE 使用前一個 CTE 的結果來構建其結果集並進一步發展。
-- Define CTE 1 WITH cte1_name AS ( SELECT col1 FROM table1_name ), -- Define CTE 2 by referring to CTE 1 cte2_name AS ( SELECT col1 FROM cte1_name ), -- Define CTE 3 by referring to CTE 2 cte3_name AS ( SELECT col1 FROM cte2_name ), -- Define CTE 4 by referring to CTE 3 cte4_name AS ( SELECT col1 FROM cte3_name ) -- Main query SELECT * FROM cte4_name |
這是另一個第二張圖和相應的程式碼。
在此示例中,CTE 3 依賴於彼此獨立的 CTE 1 和 CTE 2,而 CTE 4 依賴於 CTE 3。
-- Define CTE 1 WITH cte1_name AS ( SELECT col1 FROM table1_name ), -- Define CTE 2 cte2_name AS ( SELECT col1 FROM table2_name ), -- Define CTE 3 by referring to CTE 1 and 2 cte3_name AS ( SELECT * FROM cte1_name AS cte1 JOIN cte2_name AS cte2 ON cte1.col1 = cte2.col1 ), -- Define CTE 4 by referring to CTE 3 cte4_name AS ( SELECT col1 FROM cte3_name ) -- Main query SELECT * FROM cte4_name |
最後是更復雜的東西及其相應的程式碼:
如您所見,您可以透過多種方式連結或堆疊 CTE 來解決複雜查詢。
級別 2- 跨多個查詢
當您發現自己在多個查詢中複製和貼上 CTE 時,是時候將它們重構為檢視、UDF 或儲存過程了。
-- Define CTE 1 WITH cte1_name AS ( SELECT col1 FROM table1_name ), -- Define CTE 2 by referring to CTE 1 cte2_name AS ( SELECT col1 FROM cte1_name ), -- Define CTE 3 by referring to CTE 1 cte3_name AS ( SELECT col1 FROM cte1_name ) -- Define CTE 4 by referring to CTE 1 cte4_name AS ( SELECT col1 FROM cte1_name ), -- Define CTE 5 by referring to CTE 4 cte5_name AS ( SELECT col1 FROM cte4_name ), -- Define CTE 6 by referring to CTEs 2, 3 and 5 cte6_name AS ( SELECT * FROM cte2_name cte2 JOIN cte3_name cte3 ON cte2.column1 = cte3.column1 JOIN cte5_name cte5 ON cte3.column1 = cte5.column1 ) -- Main query SELECT * FROM cte6_name |
觀點
檢視非常適合封裝適用於許多查詢的業務邏輯。它們還用於安全應用程式,以根據終端使用者的許可權限制向終端使用者公開的行或列。
建立檢視很簡單:
CREATE OR REPLACE VIEW <view_name> AS SELECT col1 FROM table1 WHERE col1 > x; |
建立後,您可以執行:
SELECT * FROM <view_name> |
這個檢視現在儲存在資料庫中,但它不佔用任何空間(除非它被具體化)它只儲存每次從檢視中選擇或在查詢中加入檢視時執行的查詢。
檢視可以放在 CTE 內部,或者它們本身可以包含 CTE,從而建立多層模組化。這是一個看起來像的例子:
旁註: 透過組合檢視和 CTE,您可以在其他查詢中巢狀許多查詢。這不僅會對效能產生負面影響,而且某些資料庫對您可以擁有的巢狀級別有限制。
UDF
與檢視類似,您還可以將常用邏輯放入 UDF(使用者定義的函式)中。幾乎所有資料庫都允許您建立 UDF,但它們各自使用不同的程式語言來實現。
SQL Server 使用 T-SQL 建立函式。PostgreSQL 使用 PL/pgsql 或 Python(具有正確的副檔名)BigQuery 和 Snowflake 使用 Javascript、Python 等。
函式允許邏輯和變數的條件流,這使得複雜邏輯的實現變得容易。
UDF 可以返回單個標量值或表格。例如,單個標量值可用於透過正規表示式解析某些字串。
表值函式返回一個表而不是單個值。它們的行為與檢視完全一樣,但主要區別在於它們可以獲取輸入引數並基於該引數返回不同的表。很有用。
儲存過程
與表值函式一樣,儲存過程 (sprocs) 允許您將非常複雜的業務邏輯封裝在資料庫中。他們還返回表格作為輸出。
它們在事務系統中被大量使用以在資料庫內實現業務邏輯,但在資料處理中已經失寵。我不會在這裡介紹它們。
級別 3 - 超越 SQL 查詢
隨著dbt (資料構建工具)等工具的出現,您可以超越檢視和 CTE 的查詢,構建更復雜的 dag,將它們結合起來。
在 dbt 術語中,檢視和表被稱為模型。透過使用函式ref()dbt 可以引用以前構建的模型。
您還可以將 CTE 與模型混合搭配,以獲得所需的結果。
這是實踐中的樣子:
model_a.sql
select * from public.raw_data
model_b.sql
select * from {{ref('model_a')}}
如何將查詢分解為更簡單的模組
既然您已經瞭解了查詢組合背後的理論,那麼如何應用它呢?在編寫查詢時,您應用以下 3 條規則之一。它們同樣適用於所有 3 個級別。
規則 1:不要重複自己(又名 DRY 原則)
DRY 原則指出,如果您發現自己在查詢中多次複製貼上相同的程式碼塊,則應該將該程式碼放在 CTE 中並在需要的地方引用該 CTE。透過將重複的邏輯放入單獨的模組中,您可以做到輕鬆編寫、維護和除錯程式碼。
規則 2:使模組具有單一用途(也稱為 SRP 原則)
軟體工程中的單一職責原則 (SRP) 表示每個模組都應該是獨立的並且只有一個目的。該模組可以是 CTE、檢視或 dbt 模型。
透過自包含和單一職責,每個模型都可以獨立編寫、測試和除錯。Dbt 透過允許您建立宏或函式 (UDF) 來解除安裝責任,從而多次提高您的靈活性。
規則 3:將邏輯向上遊移動
當您發現自己在可能在其他地方使用的模型中實現非常具體的邏輯時,請儘快將該邏輯移至上游。
在 DAG 的世界裡,upstream 有著非常明確的含義。這意味著將潛在的通用邏輯移動到圖中較早的節點上,因為您永遠不知道哪些下游模型可能會使用它。
好的,這是足夠的理論。單擊此處檢視示例。
相關文章
- 如何將 Elixir 模組風格應用在 JS 中JS
- 關於前端模組化你應該知道的前端
- BOSHIDA AC/DC電源模組:應用於工業自動化領域
- 微前端架構將應用拆分為多個小型模組,實現模組化的設計前端架構
- [Python]OS模組應用Python
- 真棒:使用Java 11實現應用的模組化Java
- 模組化單體應用的資料隔離
- 關於模組化、元件化的理解元件化
- CSS 如何模組化,工程化CSS
- PDM應用模組設計
- iOS應用模組化的思考及落地方案(一)模組的劃分及模組化工作流程iOS
- iOS應用模組化的思考及落地方案(二)模組化自動構建工具的使用iOS
- 如何將CSS3 transforms應用於背景影象CSSS3ORM
- 如何實現css模組化CSS
- pickle模組 collections模組在物件導向中的應用物件
- 如何理解模組化鏈(Modular Chain)?AI
- Python如何將函式儲存在模組中Python函式
- Django建立app應用和admin模組DjangoAPP
- 原生應用新增 Flutter 模組依賴Flutter
- 如何將AI應用於晶片設計?谷歌有話說!AI晶片谷歌
- 如何將六西格瑪應用於教育部門?
- Nginx的HTTP模組與Stream模組:區別與應用場景NginxHTTP
- 序列化模組,隨機數模組,os模組,sys模組,hashlib模組隨機
- 關於前端模組化 CommonJS、AMD、CMD、ES6中模組載入前端JS
- 極簡實用的Asp.NetCore模組化框架新增CMS模組ASP.NETNetCore框架
- Egret應用開發實踐(03) MVC 模組化具體實現MVC
- 在Java 11中建立一個簡單的模組化應用教程Java
- Android模組化改造以及模組化通訊框架Android框架
- 如何將triz應用於人工智慧領域的產品?人工智慧
- 基於Laravel5.5的模組化開發Laravel
- [譯] 格子拼貼 — 關於模組化的故事
- 關於Android模組化你需要知道的Android
- 序列化模組,subprocess模組,re模組,常用正則
- JavaScript模組化JavaScript
- 前端模組化前端
- JS模組化JS
- ES6 模組化與 CommonJS 模組化區別JS
- webpack是如何實現前端模組化的Web前端