CodeGym /Các khóa học /SQL SELF /Ảnh hưởng của NULL lên các hàm tổng hợp: SUM(), COUNT(), ...

Ảnh hưởng của NULL lên các hàm tổng hợp: SUM(), COUNT(), AVG(), MIN(), MAX()

SQL SELF
Mức độ , Bài học
Có sẵn

Nhớ lại nhé, các hàm tổng hợp là những hàm làm việc với nhiều dòng dữ liệu cùng lúc và trả về một kết quả duy nhất. Trong PostgreSQL, bạn sẽ thường xuyên dùng các hàm tổng hợp sau:

  • SUM() — tính tổng dữ liệu.
  • AVG() — tính giá trị trung bình.
  • MIN() — tìm giá trị nhỏ nhất.
  • MAX() — tìm giá trị lớn nhất.
  • COUNT() — đếm số dòng.

Nhìn qua thì đơn giản thôi: truyền vào hàm một cột hoặc biểu thức, và nhận kết quả. Nhưng chuyện gì xảy ra nếu trong cột đó có NULL?

Hành vi của NULL trong các hàm tổng hợp: tóm tắt nhanh

Đây mới là phần thú vị nè:

  • SUM()AVG() sẽ bỏ qua NULL. Nếu có dòng nào có giá trị NULL, nó sẽ không được tính vào kết quả. Hợp lý mà, tổng làm sao tăng nếu ai đó "không đến dự tiệc"? Hoặc làm sao tính trung bình nếu thiếu một giá trị?
  • MIN()MAX() cũng bỏ qua NULL. Chúng chỉ tìm giá trị nhỏ nhất/lớn nhất trong các dòng không phải NULL. Vậy nên, khi bạn tìm nhân viên trẻ nhất mà quên điền ngày sinh, NULL sẽ không được chọn đâu.
  • COUNT(*) đếm tất cả các dòng, kể cả dòng có NULL. Nhưng COUNT(column) chỉ đếm những dòng mà cột đó có giá trị, tức là bỏ qua NULL.

Cùng xem ví dụ cho dễ hiểu nhé.

Ví dụ sử dụng hàm tổng hợp với NULL

Đây là bảng students_scores, chứa điểm kiểm tra của sinh viên:

student_id name score
1 Alysa 85
2 Bob NULL
3 Charly 92
4 Dana NULL
5 Elena 74

Giờ mình thử vài truy vấn và phân tích kết quả nhé:

  1. Tổng tất cả điểm: SUM()
SELECT SUM(score) AS total_score
FROM students_scores;

Kết quả:

total_score
251

Nhìn nè, các giá trị NULL bị bỏ qua khi tính tổng. Alysa (85), Charly (92) và Elena (74) cộng lại thành 251. Bob và Dana bị loại khỏi cuộc chơi.

  1. Điểm trung bình: AVG()
SELECT AVG(score) AS average_score
FROM students_scores;

Kết quả:

average_score
83.67

Lại nữa, NULL bị bỏ qua, và điểm trung bình chỉ tính cho ai có điểm: (85 + 92 + 74) / 3 = 83.67.

  1. Điểm nhỏ nhất và lớn nhất: MIN()MAX()
SELECT
    MIN(score) AS min_score, 
    MAX(score) AS max_score 
FROM students_scores;

Kết quả:

min_score max_score
74 92

Chỗ này cũng dễ hiểu: NULL lại bị bỏ qua, điểm nhỏ nhất là 74, lớn nhất là 92.

  1. Đếm dòng: COUNT(*) vs COUNT(column)
SELECT
    COUNT(*) AS total_rows, 
    COUNT(score) AS non_null_scores 
FROM students_scores;

Kết quả:

total_rows non_null_scores
5 3
  • COUNT(*) đếm tất cả các dòng, kể cả dòng scoreNULL.
  • COUNT(score) chỉ đếm dòng có giá trị trong cột score.

Case thực tế

Cùng xem vài ví dụ thực tế nhé.

Ví dụ 1: Đếm nhân viên có và không có lương

Giả sử bạn có bảng employees với lương của nhân viên.

id name salary
1 Alex Lin 50000
2 Maria Chi NULL
3 Anna Song 60000
4 Otto Art NULL
5 Liam Park 55000

Bạn muốn biết có bao nhiêu nhân viên đã khai báo lương, và bao nhiêu người chưa khai báo.

SELECT
    COUNT(*) AS total_employees,
    COUNT(salary) AS employees_with_salary,
    COUNT(*) - COUNT(salary) AS employees_without_salary
FROM employees;

Ở đây:

  • COUNT(*) trả về tổng số nhân viên.
  • COUNT(salary) đếm số nhân viên đã khai báo lương.
  • Để tính số nhân viên chưa có lương, chỉ cần lấy tổng trừ đi số đã khai báo.

Kết quả

total_employees employees_with_salary employees_without_salary
5 3 2

Ví dụ 2: Tính giá trung bình sản phẩm khi có giá bị thiếu

Bạn là chủ shop phép thuật, bảng products có cột price, nhưng một số sản phẩm chưa có giá.

id name price
1 Magic Wand 150
2 Enchanted Cloak NULL
3 Potion Bottle 75
4 Spell Book 200
5 Crystal Ball NULL

Bạn cần biết giá trung bình chỉ cho sản phẩm đã có giá.

SELECT AVG(price) AS average_price
FROM products;

Kết quả:

average_price
141.6667

Nếu bạn muốn đặt giá mặc định cho sản phẩm chưa có giá (ví dụ là 0), có thể dùng hàm COALESCE() (sẽ học ở bài sau).

Ví dụ 3: Tìm tuổi nhỏ nhất và lớn nhất của sinh viên

Bảng students lưu tuổi học sinh, nhưng một số bạn chưa rõ tuổi (NULL).

id name age
1 Alex Lin 20
2 Maria Chi NULL
3 Anna Song 19
4 Otto Art 22
5 Liam Park NULL

Bạn muốn biết ai trẻ nhất và ai lớn tuổi nhất.

SELECT
    MIN(age) AS youngest_student,
    MAX(age) AS eldest_student
FROM students;

Kết quả:

youngest_student eldest_student
19 22

Truy vấn này trả về tuổi nhỏ nhất và lớn nhất chỉ cho sinh viên đã có tuổi. NULL lại bị bỏ qua.

Lưu ý và bẫy thường gặp

Khi làm việc với NULL trong hàm tổng hợp, nhớ mấy điểm sau nha:

  • Trong SUM()AVG(), NULL bị bỏ qua. Có thể tận dụng để không tính giá trị "rỗng".
  • Nếu muốn đếm cả dòng có NULL trong cột, dùng COUNT(*).
  • Dùng MIN() hoặc MAX(), NULL không ảnh hưởng kết quả. Nhưng nếu cả cột đều là NULL, kết quả cũng là NULL luôn.

Mẹo khi làm việc với NULL

  1. Xem kỹ yêu cầu bài toán. Hiểu rõ có cần tính NULL không. Đôi khi, như với AVG(), bỏ qua NULL là đúng ý. Nhưng khi đếm tổng số dòng, nhớ tính cả dòng có NULL nữa.
  2. Dùng COALESCE() khi cần. Nếu muốn thay NULL bằng giá trị mặc định khi tính toán, hàm COALESCE() là bạn thân (nhưng cái này để bài sau nói kỹ hơn).
  3. Đừng nhầm COUNT(*) với COUNT(column). Đây là lỗi newbie hay gặp. Cái đầu đếm tất cả dòng, cái sau chỉ đếm dòng có giá trị khác NULL.

Giờ thì bạn đã biết NULL "im lặng" ảnh hưởng hàm tổng hợp thế nào rồi đó. Biết mấy chiêu này sẽ giúp bạn tránh bị bất ngờ và tận dụng NULL cho lợi ích của mình. Bài sau tụi mình sẽ học công cụ mạnh mẽ COALESCE() để xử lý NULL còn bá đạo hơn nữa nha.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION