AVG, MIN, MAX
Bạn sẽ học được
- Tính trung bình bằng
AVGvà hiểu nó bỏ quaNULL. - Tìm giá trị nhỏ nhất, lớn nhất bằng
MINvàMAXtrên số, ngày, chuỗi. - Biết vì sao
AVGkhác "tổng chia số dòng" khi cóNULL. - Dùng
MIN/MAXđể trả lời câu hỏi "sớm nhất", "muộn nhất", "đắt nhất". - Kết hợp ba hàm với
WHEREvàGROUP BY.
Khái niệm cốt lõi
Ba hàm này thường đi cùng nhau trong báo cáo mô tả dữ liệu:
| Hàm | Trả về | Ví dụ |
|---|---|---|
AVG(cot) | Trung bình cộng | Giá trung bình một sản phẩm |
MIN(cot) | Giá trị nhỏ nhất | Sản phẩm rẻ nhất |
MAX(cot) | Giá trị lớn nhất | Đơn hàng mới nhất |
Cả ba đều bỏ qua NULL, giống SUM. Đây là điểm khiến AVG khác phép chia thủ công:
SELECT AVG(price) AS gia_trung_binh,
SUM(price) / COUNT(*) AS chia_cho_moi_dong,
SUM(price) / COUNT(price) AS chia_cho_dong_co_gia
FROM products;Trong bảng products không có giá NULL, nên cả ba bằng nhau: 634.444,44. Nhưng nếu thêm một sản phẩm chưa có giá, COUNT(*) đếm cả dòng đó còn AVG thì không. Khi phân tích dữ liệu thiếu, hãy tự hỏi: "trung bình trên những dòng có dữ liệu, hay trên toàn bộ dòng?" — và viết truy vấn đúng theo câu trả lời.
AVG trên cột số nguyên trả về số thập phân. Cột stock có các giá trị 25, 10, 5, 0, 40, 30, 18, 12, 7, tổng 147:
SELECT AVG(stock) AS ton_kho_trung_binh FROM products;Kết quả: 16,3333.... PostgreSQL trả numeric với nhiều chữ số thập phân. Muốn gọn, dùng ROUND(AVG(stock), 1) → 16,3. Đừng nhầm ROUND là bắt buộc — nó chỉ để trình bày.
MIN và MAX không chỉ dành cho số. Với ngày, chúng cho ngày sớm nhất và muộn nhất:
SELECT MIN(order_date) AS don_som_nhat,
MAX(order_date) AS don_muon_nhat
FROM orders;Kết quả: 2025-01-05 và 2025-11-11. Đây là cách nhanh nhất để biết phạm vi thời gian của dữ liệu trước khi vẽ biểu đồ. Với chuỗi, MIN/MAX theo collation — hữu ích cho mã, kém tin cậy cho tên người có dấu.
Một điểm dễ nhầm: MIN/MAX không đi kèm dòng của chúng. Truy vấn SELECT name, MAX(price) FROM products không trả tên sản phẩm đắt nhất — nó báo lỗi trong PostgreSQL vì name không nằm trong GROUP BY và không phải hàm tổng hợp. (Các hệ quản trị khác như MySQL cũ có thể trả một giá trị tùy ý; PostgreSQL chặt chẽ hơn.) Muốn biết sản phẩm nào, dùng ORDER BY price DESC LIMIT 1 hoặc truy vấn con:
SELECT name, price
FROM products
WHERE price = (SELECT MAX(price) FROM products);Ở chương này, hãy ghi nhớ: MAX trả con số, không trả cả dòng.
AVG, MIN, MAX đều trả NULL khi không có dòng nào thỏa điều kiện — khác COUNT trả 0. Vì vậy bảng báo cáo có thể có ô trống ở nhóm không có dữ liệu. Đó là hành vi đúng; chỉ dùng COALESCE khi bạn chắc rằng 0 là câu trả lời có nghĩa.
Về hiệu năng, cả ba đều phải quét dữ liệu nếu không có chỉ mục. MIN/MAX trên cột có chỉ mục B-tree rất nhanh vì PostgreSQL đọc hai đầu chỉ mục. AVG luôn phải đọc mọi giá trị. Với bảng lớn, câu hỏi "giá cao nhất là bao nhiêu" rẻ hơn nhiều so với "giá trung bình là bao nhiêu".
Ví dụ chạy được
Mô tả giá sản phẩm trong một dòng:
SELECT MIN(price) AS gia_nho_nhat,
MAX(price) AS gia_lon_nhat,
AVG(price) AS gia_trung_binh,
COUNT(*) AS so_san_pham
FROM products;Kết quả: 180000 | 1690000 | 634444.44 | 9. Câu này là mẫu "tóm tắt một cột số" mà bạn sẽ dùng lại nhiều lần. Nó trả lời cùng lúc: khoảng giá, giá trị điển hình, và quy mô dữ liệu.
Giá trung bình chỉ tính nhóm Điện tử:
SELECT AVG(price) AS gia_tb_dien_tu
FROM products
WHERE category = 'Điện tử';Kết quả: 863.333,33. Ba sản phẩm: 890.000, 1.250.000, 450.000 — tổng 2.590.000 chia 3. So với trung bình toàn bảng 634.444,44, nhóm Điện tử đắt hơn rõ rệt. Đây là cách so sánh nhóm bằng một con số, trước khi học GROUP BY.
Trung bình giá trị một đơn hàng:
SELECT AVG(quantity * unit_price) AS gia_tri_tb_mot_dong
FROM order_items;Kết quả: 1.076.896,55. Đây là trung bình của từng dòng hàng, không phải từng đơn. Muốn trung bình theo đơn phải gom nhóm trước — nội dung bài sau. Sự khác biệt này quan trọng: một đơn có ba món góp ba giá trị vào phép trung bình, không phải một.
Ngày đăng ký sớm nhất và muộn nhất:
SELECT MIN(signup_date) AS khach_som_nhat,
MAX(signup_date) AS khach_muon_nhat
FROM customers;Kết quả: 2023-03-12 (An Nguyễn) và 2025-06-30 (Nga Lý). Dữ liệu trải hơn hai năm. Khi làm báo cáo "khách mới trong tháng", bạn sẽ dùng MAX để kiểm tra ngày gần nhất có dữ liệu hay không.
MIN/MAX theo nhóm:
SELECT category,
MIN(price) AS re_nhat,
MAX(price) AS dat_nhat
FROM products
GROUP BY category
ORDER BY category;Bốn nhóm: Điện tử 450.000–1.250.000; Gia dụng 290.000–1.690.000; Sách 180.000–180.000; Thời trang 250.000–250.000. Nhóm có một sản phẩm thì MIN bằng MAX — dấu hiệu nhóm quá nhỏ để so sánh, đáng chú ý khi đọc báo cáo.
Kết hợp AVG với điều kiện số:
SELECT AVG(quantity) AS so_luong_tb
FROM order_items
WHERE unit_price > 1000000;Chỉ còn các dòng có đơn giá trên một triệu: Bàn phím (1.250.000) và Nồi chiên (1.690.000). Các dòng tương ứng có số lượng 1, 2, 1, 1, 1, 1, 2, 1, 1... — kết quả khoảng 1,3. Điều đáng chú ý không phải con số mà là phạm vi: WHERE lọc dòng trước, AVG tính trên phần còn lại.
Thực hành
- Tìm giá thấp nhất, cao nhất và trung bình của sản phẩm còn hàng (
stock > 0). So với kết quả toàn bảng, giải thích chênh lệch. - Tính trung bình số lượng trên mỗi dòng hàng (
AVG(quantity)). Kết quả khoảng 1,86 — nghĩa là phần lớn dòng đặt một hoặc hai sản phẩm. - Tìm ngày đặt hàng đầu tiên và cuối cùng của khách 2 (Bình Trần). Kết quả: 18/01/2025 và 04/07/2025.
- Viết truy vấn trả về cả
MIN(price),MAX(price),AVG(price)và độ lệchMAX(price) - MIN(price). Khoảng giá là 1.510.000.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
column "name" must appear in the GROUP BY clause | Chọn cột không tổng hợp cùng MAX | Dùng ORDER BY ... LIMIT 1 hoặc truy vấn con |
AVG khác "tổng chia số dòng" | Có NULL trong cột | Chọn đúng mẫu số, hoặc COALESCE dữ liệu nguồn |
Kết quả NULL | Không có dòng thỏa WHERE | Kiểm tra điều kiện, dùng COALESCE nếu cần |
| Trung bình bị lệch bởi một giá trị lớn | Phân bố lệch, có ngoại lai | Dùng thêm MIN/MAX hoặc trung vị để đọc đúng |
MIN/MAX trên chuỗi ra kết quả lạ | Collation tiếng Việt | Kiểm tra collation hoặc chuẩn hóa dữ liệu |
Lỗi đầu tiên gặp rất thường khi mới học: muốn "sản phẩm đắt nhất" nên viết SELECT name, MAX(price). Hãy đọc lại: MAX trả một con số cho toàn nhóm; tên sản phẩm là thông tin của một dòng cụ thể. Muốn cả hai, phải chỉ rõ dòng đó bằng ORDER BY + LIMIT hoặc so sánh với MAX.
Tóm tắt
AVG,MIN,MAXbỏ quaNULLvà trảNULLkhi không có dòng nào.AVG= tổng chia số giá trị khácNULL, không phải chia số dòng.MIN/MAXdùng được cho số, ngày và chuỗi; trả giá trị, không trả cả dòng.- Kèm
WHEREđể tính trong một phạm vi; kèmGROUP BYđể tính theo nhóm. AVGnhạy với ngoại lai — luôn đọc kèmMIN/MAXkhi mô tả dữ liệu.
Use ← → to move between lessons