GROUP BY & HAVING
GROUP BY — จัดกลุ่มแล้ว Aggregate
Section titled “GROUP BY — จัดกลุ่มแล้ว Aggregate”GROUP BY แบ่งแถวออกเป็นกลุ่มตามค่าของคอลัมน์ แล้วทำ aggregate แยกตามกลุ่ม
เหมือนใช้ Pivot Table — ลากคอลัมน์ไปเป็น Row แล้วเลือก SUM/COUNT ใน Values
// ต้อง sort แล้ว loop ด้วยมือ// เทียบค่าก่อนหน้า ถ้าเปลี่ยนกลุ่มก็ reset counterdf.groupby("active")["price"].sum()df.groupby("active").agg({"price": ["count", "mean"]})-- นับจำนวนและค่าเรียนเฉลี่ยแยกตาม activeSELECT active, COUNT(*) AS num_students, AVG(price) AS avg_priceFROM studentsGROUP BY active;ผลลัพธ์:
| active | num_students | avg_price |
|---|---|---|
| TRUE | 4 | 5425.00 |
| FALSE | 2 | 4950.00 |
กฎสำคัญ: SELECT กับ GROUP BY
Section titled “กฎสำคัญ: SELECT กับ GROUP BY”-- ถูกต้อง: active อยู่ใน GROUP BY, COUNT เป็น aggregateSELECT active, COUNT(*) FROM students GROUP BY active;
-- error! name ไม่ได้อยู่ใน GROUP BY และไม่ใช่ aggregateSELECT active, name, COUNT(*) FROM students GROUP BY active;-- ERROR: column "name" must appear in GROUP BY clauseGROUP BY หลายคอลัมน์
Section titled “GROUP BY หลายคอลัมน์”-- จัดกลุ่มตาม active และปีที่สมัครSELECT active, EXTRACT(YEAR FROM enrolled_on) AS enroll_year, COUNT(*) AS num_students, SUM(price) AS total_priceFROM studentsGROUP BY active, EXTRACT(YEAR FROM enrolled_on)ORDER BY active DESC, enroll_year;ผลลัพธ์:
| active | enroll_year | num_students | total_price |
|---|---|---|---|
| TRUE | 2024 | 1 | 9500.00 |
| TRUE | 2025 | 3 | 12200.00 |
| FALSE | 2024 | 1 | 7800.00 |
| FALSE | 2025 | 1 | 2100.00 |
HAVING — กรองกลุ่ม
Section titled “HAVING — กรองกลุ่ม”HAVING กรองผลลัพธ์หลังจาก GROUP BY — ใช้เมื่อต้องการกรองตามค่า aggregate
-- เฉพาะกลุ่มที่มีนักเรียนมากกว่า 1 คนSELECT active, COUNT(*) AS num_studentsFROM studentsGROUP BY activeHAVING COUNT(*) > 1;WHERE vs HAVING
Section titled “WHERE vs HAVING”| WHERE | HAVING |
|---|---|
| กรอง แถว ก่อน GROUP BY | กรอง กลุ่ม หลัง GROUP BY |
| ใช้กับคอลัมน์ปกติ | ใช้กับ aggregate function |
| ทำงานก่อน | ทำงานทีหลัง |
-- WHERE กรองแถว + HAVING กรองกลุ่มSELECT active, COUNT(*) AS num_students, AVG(price) AS avg_priceFROM studentsWHERE enrolled_on >= '2025-01-01' -- กรองแถว: เฉพาะปี 2025+GROUP BY activeHAVING AVG(price) > 3000; -- กรองกลุ่ม: เฉลี่ยเกิน 3000ตัวอย่างจริง: สรุปรายเดือน
Section titled “ตัวอย่างจริง: สรุปรายเดือน”SELECT EXTRACT(MONTH FROM enrolled_on) AS month, COUNT(*) AS signups, SUM(price) AS revenueFROM studentsWHERE enrolled_on >= '2025-01-01'GROUP BY EXTRACT(MONTH FROM enrolled_on)HAVING SUM(price) > 2000ORDER BY month;