半導體企業內的數位轉型課程筆記-3-SQL
製程整合單位的數位轉型課程架構與概念-SQL

img ref: Business illustrations by Storyset
前言
SQL 是資料處理的基礎,在半導體產業數位轉型中扮演關鍵角色。原因如下列:
- 半導體產業中資料量龐大且多樣化,包括生產資料、測試資料、設備日誌和供應鏈資料。
- 這些資料通常存儲於關聯式資料庫中,而 SQL 是查詢和管理這些資料的核心工具。
- 例如:
- 晶圓製造過程中產生的巨量資料,需要通過 SQL 查詢來進行故障分析和良率統計。
- 製程資料中的 LQC(Lot Quality Control),WAT(Wafer Acceptance Test)或 CP (chip probing)資料,需要 SQL 分析來識別異常與優化參數。
- 例如:
半導體產業資料來源廣泛,可能包括 MES(Manufacturing Execution System)、ERP(Enterprise Resource Planning)、供應商資料等。這些系統的資料經常需要整合,SQL 是跨系統整合的關鍵:
- 整合生產與測試資料: 將晶圓製造過程與測試階段資料聯合起來,進行深度分析。
- 供應鏈分析: SQL 幫助整合內部資料與外部供應商資料,優化供應鏈效率。
- 跨部門協作: 透過資料庫連接,打通不同部門之間的數據孤島,提升數據共享和利用率。
此外,半導體製造的流程在每家公司、每條生產線、甚至每批次晶圓的需求都不同。 SQL 的靈活性可以幫助使用者:
- 按特定條件篩選資料,滿足不同部門的需求。
- 使用窗口函數(如
PARTITION BY)或複雜查詢進行進階分析,協助良率優化或異常檢測。
另一方面,半導體數位轉型通常伴隨視覺化工具(Power BI、Tableau)、資料科學平台(Python、R、JMP)的使用,而這些工具幾乎都依賴 SQL 來查詢數據。
- 例如:
- 使用 Power BI 對晶圓測試數據進行產生視覺化報表,後台需通過 SQL 查詢高效處理數據。
設計方向
業務需求 → 設計資料表 → 設計索引 → 測試與效能優化 → 持續維護
資料表的設置與使用是一個不停迭代的變化過程,因此修改的時候不要大幅修改, 並且在初期就確立好大致的業務需求,也不要在中期輕易地刪除掉整個資料表。
我自己的經驗是,假如不太熟悉語法,可以先用自己的語言把邏輯梳理清楚, 尤其是設置的時候常常會被業務邏輯牽著鼻子走,使用紙和筆反覆推演設計與使用情境之後, 再閱讀SQL 說明書或使用生成式AI設計語法。
我習慣使用容易繪製圖表的數位工具(如公司內的 Confluence)來記錄與呈現設計方案,這樣可以避免重複思考,並方便後續修改。
SQL語法的核心概念
DB Account Use Policy
- DDL (Data Definition Language): 建立、修改、更新、刪除資料表、索引或程序
- DML (Data Manipulation Language): 用來處理資料表裡的資料,例如寫入、更新、刪除資料
- DCL (Data Control Language): 用來控制資料表的權限,例如
GRANT,REVOKE等 - DQL (Data Query Language): 用於查詢資料的指令,不會改變資料本身內容
讓語法比較快的技巧
我覺得若只是使用資料庫抓資料的話,除了一般的select常用方法外, 活用index, join, partition by的方法萃取資料對使用者來說已經非常足夠,列舉說明如下。
- 使用
INDEX: 半導體業中,常用索引來加速對製程整合資料的查詢,例如快速檢索某機台在特定時間內的異常記錄。- 作用類似於書本中的目錄,幫助快速定位特定的資料,而不是逐行掃描整個表格。
- ref: 索引是什麼 ? 為什麼加了索引查詢會變快|| What is indexing ? How the indexing makes SELECT queries faster? | by KouWei.Lee | Wei’s Note | Medium
| 使用情境 | 不適合使用情境 |
|---|---|
| 1. 加速查詢: 適用於篩選、排序、範圍查詢和多表連接。 2. 保證資料唯一性: 使用唯一索引避免重複值。 3.全文搜尋: 搜索大文本內容(如文章內容)。 4.複雜條件篩選: 使用複合索引提升效能。 | 1.小型資料表: 列數(row) 少於數百時,效果有限,直接掃描可能更快。 2.頻繁寫入欄位: 增加寫入維護成本,例如 欄位 LastUpdatedTime 經常被更新時, 這個欄位作為索引會降低效能。3.低選擇性欄位: 例如布林值,索引效果有限,例如 IsActivate 欄位等。4. 過多索引: 會佔用過多存儲空間,且影響寫入和更新效能 |
-
使用
JOIN: 通常指的是用來合併兩個或多個表格(tables)的資料,根據它們之間的某些條件。JOIN 是資料庫查詢中非常強大且常用的功能,適用於需要從多個表格中整合資訊的情境。-
INNER JOIN: 只返回兩個表格中符合條件的資料行。也就是說,只有當表1和表2的鍵欄位有匹配值時,才會出現在結果中。
使用情境:需要從多個表格中提取有關聯的資料。 例如:查詢顧客訂單資料,顧客資訊存放在Customers表中,訂單資訊存放在Orders表中。SELECT Customers.Name, Orders.OrderID FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID; -
LEFT JOIN: 返回表1的所有資料,無論是否在表2中找到匹配。如果表2中沒有匹配的資料,則對應欄位為
NULL。
使用情境: 需要包含主表的所有資料,即使沒有相關聯的子表資料。 例如:想知道每位顧客是否有下過訂單,即使某些顧客沒有任何訂單。SELECT Customers.Name, Orders.OrderID FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID; -
RIGHT JOIN: 與 LEFT JOIN 相反,返回表2的所有資料,即使表1中沒有匹配。表1中沒有匹配的欄位將顯示為
NULL
使用情境: 需要包含子表的所有資料。 例如:查詢所有訂單記錄,即使某些訂單沒有顧客資訊(假設資料不完整)。SELECT Customers.Name, Orders.OrderID FROM Customers RIGHT JOIN Orders ON Customers.CustomerID = Orders.CustomerID; -
FULL JOIN: 返回表1和表2中的所有資料。如果某表中沒有匹配,則相應的欄位會顯示為
NULL。
使用情境: 需要整合兩個表格的所有資料,不管是否有匹配。 例如:查詢所有顧客和訂單記錄,即使有些顧客沒有訂單,有些訂單也沒有顧客資訊。SELECT Customers.Name, Orders.OrderID FROM Customers FULL JOIN Orders ON Customers.CustomerID = Orders.CustomerID; -
CROSS JOIN: 返回表1和表2的所有可能的組合,會產生笛卡兒積(Cartesian Product)。
使用情境: 需要產生所有可能的組合。 例如:列出所有商品和所有顏色的搭配。SELECT Products.ProductName, Colors.ColorName FROM Products CROSS JOIN Colors; -
SELF JOIN: 表與自身進行 JOIN,可以用別名區分表的不同角色。
使用情境: 需要比較同一表格的資料。 例如:查詢每位員工的直接主管。SELECT E1.EmployeeName AS Employee, E2.EmployeeName AS Manager FROM Employees AS E1 INNER JOIN Employees AS E2 ON E1.ManagerID = E2.EmployeeID;
-
-
使用
PARTITION BY,只要是分組進行複雜計算的資料,都可以使用此一技巧。在晶圓測試(Wafer Testing)中,針對每批晶圓(Wafer Batch)進行良率排名,可以使用 PARTITION BY。其他例子如:- 分組進行排序,包含時間的選擇,或是重複資料的選擇,像是製程整合資料中,LQC, WAT, CP資料會有同一站點重複測量的狀況,需要根據時間去抓取最新或最舊的資料,就可以使用此方法進行資料篩選
- 分組進行累積總合,像是製程整合資料中,需要針對Hold Lot的站點與時間,進行站點分組與時間累積的總合
- 計算各行的差異或偏移值,像是在製程整合資料中,會使用此方法,計算預測pilot run時間,與實際pilot run花費時間的差異
PARTITION BY 的詳細說明
我覺得partition by 算是進階的語法功能,但如果會使用這個方式的話可以讓複雜的資料組合變得更快。
如果你已經熟悉基本的 SQL 語法(如 SELECT、WHERE、JOIN、GROUP BY 等),並開始處理更複雜的資料查詢(如排名、累積值、分組分析),那學習 PARTITION BY 是非常有效果的下一步。
直接看 → trino語法說明, 其實使用情境的部分,也可以直接看trino說明書,內容是相同的,只差在例子部分。
由於不能列出我實務上真實使用的語法,所以主要用pseudo sql做解釋。
在 SQL 中,PARTITION BY 是一種分組功能,通常與窗口函數(Window Function)一起使用,
主要是在分組基礎上,執行如排名、累積總和、前後值計算等操作。 它類似於 GROUP BY,但不會聚合資料,而是保持資料的完整性並計算窗口函數結果。
相較於簡單的聚合函數(如 SUM、COUNT),窗口函數提供更多彈性, 特別是當需要保留行的完整性並對每行執行計算時。
學習 PARTITION BY 的建議
- 先熟悉窗口函數(Window function):
- 了解常見窗口函數(如
ROW_NUMBER()、RANK()、SUM()等)如何與PARTITION BY配合工作。
- 了解常見窗口函數(如
- 逐步練習:
- 從簡單的分組排名開始,例如
ROW_NUMBER() OVER (PARTITION BY 分組欄位)。 - 然後學習複雜的應用,如累積總和或差異計算。
- 從簡單的分組排名開始,例如
- 實際業務場景: 在報表生成、多維度數據分析(如每月銷售額趨勢)或處理比較多資料的情境中嘗試使用。
語法結構
<Window Function>(column_name) OVER (PARTITION BY grouping_column
ORDER BY sorting_column)| EmployeeID | DepartmentID | Salary |
|---|---|---|
| 101 | 1 | 5000 |
| 102 | 1 | 4500 |
| 103 | 2 | 6000 |
| 104 | 2 | 5800 |
SELECT
EmployeeID,
DepartmentID,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank
FROM Employees;
結果:
- 資料根據
DepartmentID被劃分為兩個分區:- 第一個分區:
DepartmentID = 1。 - 第二個分區:
DepartmentID = 2。
- 第一個分區:
ROW_NUMBER()函數分別在每個分區內執行,且排名不會跨越分區。
| EmployeeID | DepartmentID | Rank |
|---|---|---|
| 101 | 1 | 1 |
| 102 | 1 | 2 |
| 103 | 2 | 1 |
| 104 | 2 | 2 |
| Component | Description |
|---|---|
<Window Function> | 例如:ROW_NUMBER()、RANK()、SUM()、AVG() 等。 |
| PARTITION BY | 指定分組欄位: 使用 PARTITION BY 關鍵字指定一個欄位,根據該欄位的值將資料分組。例如,按部門(DepartmentID)將員工分組。將資料分區: 資料會被劃分成多個邏輯分區,每個分區包含相同分組欄位值的資料。 例如: DepartmentID = 1 是一個分區。 DeartmentID = 2 是另一個分區。每個分區獨立應用窗口函數: 窗口函數會在每個分區內執行計算,這些計算不會跨分區。例如,為每個部門內的員工計算排名或累積總和,其他部門的計算結果不會受到影響。 |
| ORDER BY | (可選)指定分區內資料的排序方式,影響窗口函數的執行順序。 |
PARTITION BY 的使用情境
- 賦予每組唯一的行號
使用 ROW_NUMBER() 為每個分組的行生成唯一編號。
SELECT
EmployeeID,
DepartmentID,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC)
AS RowNum
FROM Employees;
結果範例:
| EmployeeID | DepartmentID | Salary | RowNum |
|---|---|---|---|
| 101 | 1 | 5000 | 1 |
| 102 | 1 | 4500 | 2 |
| 201 | 2 | 6000 | 1 |
| 202 | 2 | 5800 | 2 |
解釋: 資料按 DepartmentID 分區,並在每個分區內按 Salary 由高到低排序,生成唯一行號。
計算每組的累積和
使用 SUM() 計算每組的累積總和。
SELECT
CustomerID,
OrderDate,
SUM(OrderAmount) OVER (PARTITION BY CustomerID ORDER BY OrderDate)
AS RunningTotal
FROM Orders;
結果範例:
| CustomerID | OrderDate | OrderAmount | RunningTotal |
|---|---|---|---|
| C1 | 2024-01-01 | 100 | 100 |
| C1 | 2024-02-01 | 200 | 300 |
| C2 | 2024-01-15 | 150 | 150 |
| C2 | 2024-03-01 | 250 | 400 |
解釋: 按 CustomerID 分區,並按 OrderDate 排序,累積計算每位客戶的總支出。
計算分組內的排名
使用 RANK() 或 DENSE_RANK() 為每組資料分配排名。
SELECT
EmployeeID,
DepartmentID,
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC)
AS Rank
FROM Employees;
結果範例:
| EmployeeID | DepartmentID | Salary | Rank |
|---|---|---|---|
| 101 | 1 | 5000 | 1 |
| 102 | 1 | 4500 | 2 |
| 201 | 2 | 6000 | 1 |
| 202 | 2 | 6000 | 1 |
解釋: 資料按 DepartmentID 分區,並根據 Salary 排名,RANK() 處理相同分數時會跳過名次。
計算每組的總數或平均數
使用 COUNT() 或 AVG() 等函數計算分區內的統計值。
SELECT
EmployeeID,
DepartmentID,
COUNT(*) OVER (PARTITION BY DepartmentID) AS TotalEmployees,
AVG(Salary) OVER (PARTITION BY DepartmentID) AS AvgSalary
FROM Employees;
結果範例:
| EmployeeID | DepartmentID | TotalEmployees | AvgSalary |
|---|---|---|---|
| 101 | 1 | 3 | 4666.67 |
| 102 | 1 | 3 | 4666.67 |
| 103 | 1 | 3 | 4666.67 |
| 201 | 2 | 2 | 5900.00 |
| 202 | 2 | 2 | 5900.00 |
解釋: 每個部門計算員工總數和平均薪水,並將結果應用於每行。
計算各行的差異或偏移值
使用 LAG() 或 LEAD() 計算分區內的前後值差異。
SELECT
EmployeeID,
DepartmentID,
Salary,
LAG(Salary) OVER (PARTITION BY DepartmentID ORDER BY Salary DESC)
AS PrevSalary
FROM Employees;
結果示例:
| EmployeeID | DepartmentID | Salary | PrevSalary |
|---|---|---|---|
| 101 | 1 | 5000 | NULL |
| 102 | 1 | 4500 | 5000 |
| 103 | 1 | 4000 | 4500 |
解釋: 每個部門按薪資降序排列,並計算每位員工的前一位薪資。
PARTITION BY 與 GROUP BY 的區別
| 功能 | PARTITION BY | GROUP BY |
|---|---|---|
| 保留原始資料 | 保留資料的完整性,分區後每行資料仍然存在。 | 將資料匯總後僅返回每組的聚合結果,每行代表一組。 |
| 應用範圍 | 用於窗口函數,如 ROW_NUMBER()、RANK()、SUM() 等。 | 用於聚合函數,如 SUM()、COUNT() 等,但不適用於窗口函數。 |
| 查詢結果 | 每行都會包含計算結果(如行號、累積和等),原始資料不會丟失。 | 返回每組的匯總結果,原始資料可能被壓縮。 |
| 應用場景 | 需要在分組內執行排序、排名、累積計算或前後值差異計算。 | 需要分組後直接計算匯總結果,例如總和、平均值、筆數等。 |
PARTITION BY 與 GROUP BY的比較
| 比較項目 | PARTITION BY(分區) | GROUP BY(分組) |
|---|---|---|
| 用途 | 將資料根據分區欄位劃分,並對每個分區內的資料執行窗口函數計算,而不聚合資料行。 | 將資料根據分組欄位進行匯總,每組只返回一行結果,適用於匯總計算(例如總和、平均值)。 |
| 保留資料行 | 保留所有資料行,每行資料分區後獨立計算 | 將每個分組內的資料匯總,結果的資料行數通常少於原始表的行數 |
| 適用情境 | 適用於排名、累積總和、移動平均值等窗口函數計算的進階分析,以及需要分區內進行排序的情境。 | 適用於簡單匯總,例如計算總和、平均值、筆數等 |
| 輸出結果 | 保留完整的原始資料,分區後每行資料仍然存在。會返回所有資料行,並在分區內新增計算結果作為額外的欄位。 | 將資料匯總後,僅返回每組的聚合結果,每行代表一組。 |
| 使用的函數 | 用於窗口函數,如 ROW_NUMBER()、RANK()、SUM() OVER、LAG() 等。 | 用於聚合函數,如 SUM()、COUNT()、AVG()、MAX()、MIN() 等。 |
| 分組/分區方式 | 使用 PARTITION BY 將資料分區,分區內可繼續執行其他操作,如排序。 | 使用 GROUP BY 將資料分組,分組後只能執行匯總操作。 |
| 是否可以分組內排序 | 可在 PARTITION BY 基礎上使用 ORDER BY,決定分區內的計算順序(例如排名或累積總和)。 | 只對最終結果集排序,分組內無排序操作。 |
| 範例 | 計算每個部門內的排名 SELECT EmployeeID, DepartmentID, ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank FROM Employees; | 計算每個部門的總薪資SELECT DepartmentID, SUM(Salary) AS TotalSalary FROM Employees GROUP BY DepartmentID; |
| 效能差異 | 計算更複雜,適合進階分析,效能受排序和分區影響。 | 效能較高,適用於數據量大且僅需匯總結果的場景。 |
| 使用場景重點 | 分析型報表:累積銷售額、分組排名等,或是需要保留原始資料的分析情境 | 匯總型報表:每月銷售總額、客戶總數等。 |
連接資料庫注意的事項
- 注意driver是否有設置,例如在使用Power BI連接資料庫時,確認Windows ODBC driver中,是否有設置該資料庫的驅動與port
- 注意連接資料庫的資訊是否正確,例如在使用Python連接資料庫時,host和port是否正確
總結
SQL 是數位轉型中的核心技能,熟練掌握 INDEX、 JOIN與 WINDOW FUNCTION等進階功能,可以幫助處理更複雜的情境。透過這些工具的結合,在半導體產業中萃取資料與製作報表的步驟流程裡,可以顯著提升資料分析與自動化決策能力。
推薦閱讀
十分鐘內快速上手與使用 Window function|SQL/PostgreSQL 教學