CodeGym /課程 /SQL SELF /簡單 CTE 做資料準備:範例跟真實案例

簡單 CTE 做資料準備:範例跟真實案例

SQL SELF
等級 27, 課堂 2
開放

簡單 CTE 做資料準備:範例跟真實案例

你大概已經搞懂 CTE 的基本用法,甚至寫 WITH 都快變成肌肉記憶了。今天我們來更深入一點——看看怎麼在真實情境下用 CTE 做資料準備。想像一下你要做一份報表或寫一個複雜的 SQL 查詢:第一步就是把原料拆開——然後才開始煮出好吃的分析「湯」。

CTE 在這裡超好用,因為它可以做中間步驟:像是篩選、計算、聚合、算平均值——反正資料準備要用到的都能搞定。你可以把複雜查詢拆成幾個很清楚的邏輯區塊,每個區塊只做一件事:挑出需要的資料、算平均、或是幫最後查詢準備好資料。這樣不只程式碼好讀,也不用一直複製貼上重複的片段,更不用硬生生建一堆 temp table(如果你根本不想要的話)。

這種 CTE 的寫法特別適合你要做報表、做複雜篩選,或是想在進一步處理前先「清理」資料。這時候 CTE 不只是個技術小技巧,而是完整的策略——一步一步把邏輯排好,過程都在你掌控之中。

準備好了嗎?我們馬上來看範例。

用 CTE 篩選資料

CTE 超適合把你要的資料「撈」出來,這樣後面就只要處理你真的需要的東西。你不用寫一堆巢狀查詢,先把資料篩出來、給這個步驟取個名字——然後就可以像用一般 table 一樣用這個結果。

假設我們有一張 students 表,裡面存學生的成績:

表 students

student_id first_name last_name grade
1 Otto Lin 87
2 Maria Chi 92
3 Alex Ming 79
4 Anna Song 95

假設你想挑出成績大於 85 的人。用 CTE 做這件事超直觀:

WITH excellent_students AS (
    SELECT student_id, first_name, last_name, grade
    FROM students
    WHERE grade > 85
)
SELECT * FROM excellent_students;

結果:

student_id first_name last_name grade
1 Otto Lin 87
2 Maria Chi 92
4 Anna Song 95

這裡有什麼方便的?

你先把要的資料挑出來,給這個步驟一個名字——excellent_students。這個結果你可以繼續用:像是跟別的表 join、再做一次篩選、或是算平均成績。整個查詢很清楚、簡單又不會亂掉,特別是查詢很大的時候。

用 CTE 做資料聚合

接下來我們來看要怎麼算資料筆數或平均值。比如說,我們有一張 enrollments 表,記錄學生選了哪些課。

表 enrollments

student_id course_id
1 101
2 102
3 101
4 103
2 101

我們想知道每門課有多少學生選。

查詢範例:

WITH course_enrollments AS (
    SELECT course_id, COUNT(student_id) AS student_count
    FROM enrollments
    GROUP BY course_id
)
SELECT * FROM course_enrollments;

結果:

course_id student_count
101 3
102 1
103 1

這裡重點:

  • 我們用 course_id 分組,算出每門課有幾個學生。
  • 表 course_enrollments 現在就有這些資訊,可以拿來做進一步分析。

準備報表資料

如果你要做一份詳細報表,需要經過好幾步資料處理,CTE 就是你的好朋友。它可以把整個邏輯拆成幾個很清楚的區塊,而且不用多建一堆 temp table。想像你有一張 grades 表存成績,還有一張 students 表存學生資料。你要做一份報表,只列出平均分數超過 80 的學生。

表 grades

student_id grade
1 90
1 85
2 92
3 78
3 80
4 95

表 students

student_id first_name last_name
1 Otto Lin
2 Maria Chi
3 Alex Ming
4 Anna Song

不用寫很長的巢狀查詢,可以一步一步來:

WITH avg_grades AS (
    SELECT student_id, AVG(grade) AS avg_grade
    FROM grades
    GROUP BY student_id
    HAVING AVG(grade) > 80
),
students_with_grades AS (
    SELECT s.student_id, s.first_name, s.last_name, ag.avg_grade
    FROM students s
    JOIN avg_grades ag ON s.student_id = ag.student_id
)
SELECT * FROM students_with_grades;

第一步(avg_grades)我們算出每個學生的平均分數,直接只留成績好的(超過 80)。第二步(students_with_grades)把這些資料跟 students 表 join 起來,拿到名字跟姓氏。最後 SELECT 出來的表格就可以直接貼進報表——都算好了、篩好了、排版也很漂亮。

結果:

student_id first_name last_name avg_grade
1 Otto Lin 87.5
2 Maria Chi 92.0
4 Anna Song 95.0

這種寫法就是 CTE 的強大之處:你可以專心在邏輯跟結構,不用分心去建或刪 temp table 這種雜事。

計算複雜指標

有時候你要在一個查詢裡組合不同的資料。比如說,我們要算每門課:

  1. 學生人數。
  2. 課程平均分數。

查詢範例:

WITH course_counts AS (
    SELECT course_id, COUNT(student_id) AS student_count
    FROM enrollments
    GROUP BY course_id
),
course_avg_grades AS (
    SELECT e.course_id, AVG(g.grade) AS avg_grade
    FROM enrollments e
    JOIN grades g ON e.student_id = g.student_id
    GROUP BY e.course_id
)
SELECT cc.course_id, cc.student_count, cag.avg_grade
FROM course_counts cc
JOIN course_avg_grades cag ON cc.course_id = cag.course_id;

常見錯誤要避開

用 CTE 的時候,其實很容易踩到幾個常見地雷。

第一個——過度 materialization。如果你建太多 CTE,PostgreSQL 可能會把它們的結果存成 temp table,就算你只用一次。結果查詢會變慢,完全沒必要。

第二個錯誤——濾掉資料的順序搞錯。如果你在不同階段亂加 filter,最後查出來的資料可能跟你想的不一樣。比如說,你可能太早就把重要資料濾掉了。

所以 CTE 最好用在資料要經過幾個連續轉換的時候——這時候它的優點才會完全發揮,讓你寫出乾淨、好懂又有效率的 code。

2
任務
SQL SELF, 等級 27, 課堂 2
上鎖
使用 CTE 選出高薪員工
使用 CTE 選出高薪員工
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION