Hàm COUNT()
Xem thửTình huống mở đầu
Cuối ngày, chủ cửa hàng shop hỏi bạn ba câu: hôm nay có bao nhiêu đơn, bao nhiêu đơn có ghi chú của khách, và có bao nhiêu khách khác nhau đã mua. Bảng orders hôm nay có 5 đơn. Khách số 10 mua ba lần, hai khách còn lại mỗi người mua một lần, và chỉ 2 đơn có ghi chú.
Hãy đoán trước ba con số, rồi đọc tiếp. Có một hàm đếm cho cả ba câu, nhưng mỗi câu cần một cách viết khác nhau, và chọn nhầm cách viết sẽ cho một con số hợp lệ nhưng sai ý.
Ôn nhanh bài trước: INSERT một dòng có khóa chính là NULL vào bảng products gây lỗi mã nào?
Đáp án: lỗi 1048, vì khóa chính không bao giờ được NULL. Cột note của bài này thì ngược lại, được phép NULL, và hàm COUNT đối xử với NULL theo một quy tắc riêng.
Mục tiêu bài học
Sau bài này bạn có thể:
- Viết
COUNT(*),COUNT(cột)vàCOUNT(DISTINCT cột), và nói được mỗi cách đếm cái gì. - Dự đoán kết quả của
COUNTkhi cột cóNULLvà khi có chuỗi rỗng. - Kết hợp
WHEREđể đếm theo điều kiện, vàGROUP BYđể đếm theo nhóm. - Nhận ra hai bẫy thường gặp: chọn nhầm
COUNT(*)vớiCOUNT(cột), và quênGROUP BY.
Bức tranh lớn
Xuyên suốt bài, mỗi dòng của bảng orders là một phiếu đơn hàng trong sổ cuối ngày, và COUNT là người kiểm phiếu đứng đếm. Dự án xuyên suốt là báo cáo cuối ngày của cửa hàng shop.
Hình dưới trả lời một câu hỏi: dựa vào cách viết bên trong dấu ngoặc của COUNT, người kiểm phiếu đếm cái gì?
Đang tải sơ đồ…
Cách đọc hình: đi từ ô xanh dương xuống. Ô cam đầu tiên hỏi trong ngoặc có phải dấu sao không: nếu phải thì đếm mọi dòng còn lại sau WHERE (ô tím bên trái). Nếu là một cột, ô cam thứ hai hỏi có DISTINCT không: không có thì đếm dòng mà cột khác NULL, có thì đếm số giá trị khác nhau của cột và vẫn bỏ NULL.
Chỗ ẩn dụ không còn đúng: người kiểm phiếu thật nhìn thấy cả tờ phiếu, còn COUNT(cột) chỉ nhìn vào một ô. Phiếu có ô đó trống thì với COUNT(cột) coi như phiếu không tồn tại.
Khái niệm cốt lõi
Bài này có bốn khái niệm.
1. COUNT(*) đếm dòng
COUNT(*) đếm mọi dòng của kết quả, không quan tâm cột nào có giá trị hay thiếu. Đây là cách viết để hỏi "có bao nhiêu dòng".
2. COUNT(cột) đếm giá trị khác NULL
COUNT(cột) chỉ đếm những dòng mà cột đó khác NULL. Chuỗi rỗng '' là một giá trị thật nên vẫn được đếm. Đây là cách viết để hỏi "bao nhiêu dòng có giá trị ở cột này".
3. COUNT(DISTINCT cột) đếm giá trị khác nhau
DISTINCT gom các giá trị trùng thành một trước khi đếm, và vẫn bỏ NULL. Đây là cách viết để hỏi "có bao nhiêu giá trị khác nhau", ví dụ số khách khác nhau.
4. WHERE lọc trước, GROUP BY chia nhóm
WHERE loại dòng trước, rồi COUNT mới đếm phần còn lại. GROUP BY chia các dòng thành nhóm, và COUNT đếm riêng từng nhóm.
Ví dụ đầu tiên
Đầu tiên, dựng sổ đơn hàng hôm nay với 5 đơn [Đã chạy: MySQL 8.0.44]:
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
total INT NOT NULL,
note VARCHAR(100) -- ghi chú tùy chọn, được phép NULL
);
INSERT INTO orders VALUES
(1, 10, 150000, 'fast delivery'),
(2, 10, 90000, NULL),
(3, 11, 200000, 'gift wrap'),
(4, 12, 60000, NULL),
(5, 10, 120000, NULL);
+----+-------------+--------+---------------+
| id | customer_id | total | note |
+----+-------------+--------+---------------+
| 1 | 10 | 150000 | fast delivery |
| 2 | 10 | 90000 | NULL |
| 3 | 11 | 200000 | gift wrap |
| 4 | 12 | 60000 | NULL |
| 5 | 10 | 120000 | NULL |
+----+-------------+--------+---------------+
Câu SELECT * FROM orders ORDER BY id cho ra bảng trên: ba đơn có note là NULL, hai đơn có ghi chú thật. Giờ trả lời ba câu của chủ cửa hàng, từng câu một. Câu đầu tiên là số đơn:
SELECT COUNT(*) AS all_orders FROM orders;
Dự đoán trước: kết quả là mấy?
+------------+
| all_orders |
+------------+
| 5 |
+------------+
Câu thứ hai là số đơn có ghi chú:
SELECT COUNT(note) AS with_note FROM orders;
+-----------+
| with_note |
+-----------+
| 2 |
+-----------+
Câu thứ ba là số khách khác nhau:
SELECT COUNT(DISTINCT customer_id) AS buyers FROM orders;
+--------+
| buyers |
+--------+
| 3 |
+--------+
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
COUNT(*) AS all_orders | Đếm cả 5 dòng của bảng | Dấu sao nghĩa là đếm dòng, không xét cột nào |
COUNT(note) AS with_note | Đếm dòng mà note khác NULL, ra 2 | Ba đơn có note là NULL bị bỏ qua, nên đây là số ghi chú chứ không phải số đơn |
COUNT(DISTINCT customer_id) AS buyers | Gom khách trùng lại, ra 3 | Khách 10 xuất hiện ba lần nhưng chỉ được đếm một lần |
AS all_orders, AS with_note, AS buyers | Đặt tên cho cột kết quả | Không đặt tên thì tiêu đề cột là chính biểu thức, khó đọc trong báo cáo |
Cả ba hàm có thể viết chung trong một câu SELECT, và đó cũng là báo cáo cuối ngày hoàn chỉnh [Đã chạy: MySQL 8.0.44]:
SELECT COUNT(*) AS all_orders,
COUNT(note) AS with_note,
COUNT(DISTINCT customer_id) AS buyers
FROM orders;
+------------+-----------+--------+
| all_orders | with_note | buyers |
+------------+-----------+--------+
| 5 | 2 | 3 |
+------------+-----------+--------+
Xem từng bước
Animation dưới đây cho người kiểm phiếu đi qua từng đơn và cập nhật cả ba con số. Hãy trả lời câu dự đoán trước khi bấm bước đầu tiên.
Đang tải Animation…
Bảng trace bằng chữ [Đã chạy: MySQL 8.0.44, bảng orders có 5 đơn]:
| Đơn | customer_id | note | COUNT(*) | COUNT(note) | Khách khác nhau |
|---|---|---|---|---|---|
| 1 | 10 | fast delivery | 1 | 1 | 1 |
| 2 | 10 | NULL | 2 | 1 | 1 |
| 3 | 11 | gift wrap | 3 | 2 | 2 |
| 4 | 12 | NULL | 4 | 2 | 3 |
| 5 | 10 | NULL | 5 | 2 | 3 |
Bạn vừa làm được: giải thích vì sao ba hàm COUNT trên cùng một bảng cho ba con số 5, 2 và 3.
Hai cách mở rộng thường dùng. WHERE lọc dòng trước khi đếm, ví dụ đếm các đơn từ 100000 trở lên:
SELECT COUNT(*) AS big_orders
FROM orders
WHERE total >= 100000; -- WHERE lọc dòng trước, COUNT đếm sau
+------------+
| big_orders |
+------------+
| 3 |
+------------+
GROUP BY đếm riêng cho từng nhóm, ví dụ số đơn của mỗi khách:
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
ORDER BY customer_id;
+-------------+-------------+
| customer_id | order_count |
+-------------+-------------+
| 10 | 3 |
| 11 | 1 |
| 12 | 1 |
+-------------+-------------+
Concept Check
Bảng orders có 5 đơn như trên: 3 đơn có note là NULL, 3 khách khác nhau.
Câu 1. SELECT COUNT(note) FROM orders cho kết quả nào?
- A. 5, vì
COUNTluôn đếm mọi dòng của bảng. - B. 3, vì ba đơn không có ghi chú là những đơn cần đếm.
- C. 2, vì chỉ hai đơn có
notekhácNULLđược đếm. - D.
NULL, vì cộtnotecó giá trịNULLtrong bảng.
Câu 2. Muốn biết số khách khác nhau đã mua hôm nay, viết thế nào?
- A.
COUNT(*), vì mỗi đơn là một khách khác nhau. - B.
COUNT(customer_id), vì cột này đếm cả khách trùng một lần. - C.
COUNT(DISTINCT note), vì ghi chú xác định khách. - D.
COUNT(DISTINCT customer_id), vì cần gom khách trùng lại.
Câu 3. Một đơn có note là chuỗi rỗng '' (không phải NULL). COUNT(note) có đếm đơn đó không?
- A. Có, vì chuỗi rỗng là một giá trị thật, khác
NULL. - B. Không, vì chuỗi rỗng không có ký tự nào để đếm.
- C. Không, vì chuỗi rỗng bị MySQL tự đổi thành
NULL. - D. Có, nhưng chỉ khi thêm
DISTINCTvào trong ngoặc.
Đáp án và giải thích:
- Câu 1: C. A sai vì chỉ
COUNT(*)mới đếm mọi dòng. B nhầm với số đơn không có ghi chú, đó là 5 trừ 2. D sai vìCOUNTkhông bao giờ trả vềNULL. - Câu 2: D. A sai vì khách 10 mua ba đơn. B sai vì
COUNT(customer_id)đếm mọi dòng cócustomer_id, tức 5. C sai vì ghi chú không liên quan đến khách. - Câu 3: A. B và C sai vì
COUNTchỉ phân biệtNULLvà khôngNULL, MySQL không tự đổi chuỗi rỗng thànhNULL. D sai vìDISTINCTkhông ảnh hưởng đến việc chuỗi rỗng có được đếm.
Làm cùng mình
Chủ cửa hàng muốn một báo cáo duy nhất cho cả ba câu hỏi, rồi hỏi thêm: "bao nhiêu đơn không có ghi chú?"
Bước 1. Vẽ ra giấy
Trước khi gõ lệnh, hãy viết ba con số bạn kỳ vọng cho báo cáo (số đơn, số ghi chú, số khách) và một con số cho câu hỏi thêm. Gợi ý: có hai cách ra cùng đáp án cho câu hỏi thêm, một cách trừ hai con số COUNT, một cách dùng WHERE.
Bước 2. Điền chỗ trống
Điền hai chỗ trống bằng hàm COUNT phù hợp:
SELECT COUNT(*) AS all_orders
-- TODO 1: thêm cột đếm số đơn có ghi chú, đặt tên with_note
-- TODO 2: thêm cột đếm số khách khác nhau, đặt tên buyers
FROM orders;
Gợi ý: nhớ thêm dấu phẩy sau biểu thức đứng trước.
Bước 3. Đếm số đơn không có ghi chú
Viết ba cách ra cùng con số: trừ hai COUNT, dùng SUM(CASE ...), hoặc dùng COUNT(*) kèm WHERE note IS NULL. Dự đoán trước: ba cách cho cùng con số nào? Sau đó so với kết quả ở mục Lời giải và đáp án.
Bước 4. Vẽ lại ra giấy
Ghi một câu: vì sao COUNT(note) không thể dùng để trả lời "có bao nhiêu đơn".
Thử phá nó đi
Ba lỗi người mới hay gặp với COUNT [Đã chạy: MySQL 8.0.44]:
| Code lỗi | Thông báo hoặc hiện tượng | Nguyên nhân | Cách sửa |
|---|---|---|---|
SELECT COUNT(note) để đếm đơn | Ra 2, không có lỗi | COUNT(cột) bỏ qua NULL nên đếm ghi chú chứ không đếm đơn | Dùng COUNT(*) để đếm dòng |
SELECT customer_id, COUNT(*) không có GROUP BY | ERROR 1140: In aggregated query without GROUP BY... | Trộn cột thường với hàm tổng hợp mà không nói gom theo cột nào | Thêm GROUP BY customer_id |
COUNT(DISTINCT *) | ERROR 1064: You have an error in your SQL syntax | DISTINCT cần một cột hoặc biểu thức cụ thể, không nhận dấu sao | Chọn cột cần đếm, ví dụ COUNT(DISTINCT customer_id) |
Lỗi thứ hai:
SELECT customer_id, COUNT(*) FROM orders;
ERROR 1140 (42000) at line 15: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'shop.orders.customer_id'; this is incompatible with sql_mode=only_full_group_by
Lỗi thứ ba:
SELECT COUNT(DISTINCT *) FROM orders;
ERROR 1064 (42000) at line 15: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*) FROM orders' at line 1
Một bẫy khó thấy hơn không báo lỗi: đếm số đơn của từng khách bằng LEFT JOIN. Thêm bảng customers có thêm khách Dung (id 13) chưa mua gì. So sánh COUNT(*) với COUNT(o.id) [Đã chạy: MySQL 8.0.44]:
SELECT c.name,
COUNT(*) AS star_count,
COUNT(o.id) AS real_orders
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY c.id;
+------+------------+-------------+
| name | star_count | real_orders |
+------+------------+-------------+
| An | 3 | 3 |
| Binh | 1 | 1 |
| Chi | 1 | 1 |
| Dung | 1 | 0 |
+------+------------+-------------+
Dung chưa có đơn nào nhưng COUNT(*) vẫn ra 1, vì LEFT JOIN giữ lại một dòng cho Dung với cột của orders là NULL. COUNT(o.id) bỏ dòng NULL đó nên ra đúng 0.
Hình dưới đối chiếu mô hình sai với mô hình đúng về COUNT(cột).
Đang tải sơ đồ…
Cách đọc hình: hai chuỗi độc lập. Chuỗi đỏ là niềm tin sai rằng COUNT(note) đếm đơn, dẫn đến báo cáo ghi 2 đơn trong khi cửa hàng bán 5. Chuỗi xanh phân vai rõ: COUNT(*) cho số đơn, COUNT(note) cho số ghi chú.
Tự tay làm
Mỗi bài nêu đầu vào, kết quả mong đợi và tiêu chí đạt.
Cơ bản
Bài 1. Điền bảng trace. Đầu vào: bảng orders hôm qua có 6 đơn.
| id | customer_id | total | note |
|---|---|---|---|
| 1 | 20 | 100000 | gift wrap |
| 2 | 20 | 80000 | gift wrap |
| 3 | 21 | 50000 | NULL |
| 4 | 22 | 300000 | fast delivery |
| 5 | 22 | 70000 | NULL |
| 6 | 23 | 90000 | chuỗi rỗng |
Điền kết quả của bốn biểu thức:
| Biểu thức | Kết quả |
|---|---|
COUNT(*) | ? |
COUNT(note) | ? |
COUNT(DISTINCT note) | ? |
COUNT(DISTINCT customer_id) | ? |
Tiêu chí đạt: đúng cả bốn ô. Gợi ý: chuỗi rỗng là một giá trị thật, và DISTINCT coi hai ghi chú giống hệt nhau là một.
Vận dụng
Bài 2. Đơn lớn. Đầu vào: bảng orders hôm nay (5 đơn ở đầu bài). Viết một câu SELECT trả về số đơn có total từ 100000 trở lên và số khách khác nhau trong số các đơn đó. Tiêu chí đạt: kết quả hai con số, và bạn kiểm tra được bằng cách đọc lại bảng. Gợi ý: WHERE đặt trước, rồi hai hàm COUNT cùng đếm trên phần còn lại.
Thử thách (tùy chọn)
Bài 3. Khách chưa mua gì. Đầu vào: bảng customers có bốn khách (An 10, Binh 11, Chi 12, Dung 13) và bảng orders hôm nay. Viết câu trả về tên mỗi khách và số đơn của họ, trong đó Dung phải ra 0. Tiêu chí đạt: bốn dòng, mỗi dòng một khách, Dung có 0. Gợi ý: LEFT JOIN giữ khách chưa có đơn, và hãy chọn đúng cột bên trong COUNT.
Tự kiểm tra
Trả lời bằng lời của bạn, không nhìn lại bài. Mỗi câu ghi mục tiêu được kiểm tra.
- (Mục tiêu 1, nhớ lại)
COUNT(*),COUNT(cột)vàCOUNT(DISTINCT cột)lần lượt đếm cái gì? - (Mục tiêu 2, dự đoán) Bảng 4 dòng, cột
xcó giá trị 1, 1,NULL, 2. Ba biểu thứcCOUNT(*),COUNT(x),COUNT(DISTINCT x)ra bao nhiêu? - (Mục tiêu 2, dự đoán trạng thái)
COUNT(*)trên bảng có 0 dòng ra bao nhiêu,NULLhay 0? - (Mục tiêu 3, giải thích) Vì sao
WHEREđặt trướcCOUNTlại ảnh hưởng tới kết quả đếm? - (Mục tiêu 4, tìm lỗi)
SELECT customer_id, COUNT(*) FROM ordersbị lỗi gì và sửa thế nào?
Điều cần nhớ
COUNT(*)đếm dòng,COUNT(cột)đếm dòng có giá trị khácNULL,COUNT(DISTINCT cột)đếm số giá trị khác nhau.- Chuỗi rỗng là một giá trị thật nên được đếm,
NULLthì không. WHERElọc dòng trước khi đếm,GROUP BYcho một con số đếm riêng cho mỗi nhóm.- Dùng
LEFT JOINđể đếm theo nhóm có thể rỗng thì phải đếm cột của bảng bên phải, không đếm dấu sao. - Hàm
COUNTkhông bao giờ trả vềNULL: bảng không có dòng nào cho ra 0.
Cheat sheet:
| Muốn | Viết | Kết quả |
|---|---|---|
| Số đơn | COUNT(*) | Đếm mọi dòng |
| Số đơn có ghi chú | COUNT(note) | Bỏ dòng note là NULL |
| Số đơn không có ghi chú | COUNT(*) - COUNT(note) | Hiệu hai con số đếm |
| Số khách khác nhau | COUNT(DISTINCT customer_id) | Mỗi khách đếm một lần |
| Số đơn của từng khách | COUNT(*) kèm GROUP BY customer_id | Một dòng cho mỗi khách |
| Số đơn của khách có thể chưa mua | COUNT(o.id) sau LEFT JOIN | Khách chưa mua ra 0 |
Hình tóm tắt của bài là sơ đồ ở mục Bức tranh lớn.
Câu hỏi để dành cho bài sau: chủ cửa hàng hỏi tiếp "hôm nay thu được bao nhiêu tiền?". Đếm đơn không trả lời được câu đó. Hàm nào cộng các con số trong cột total?
Đào sâu
Phần này dành cho bạn tò mò.
- Cơ chế bên trong.
COUNT(1)vàCOUNT(*)cho cùng một kết quả, vì1là hằng số khácNULLở mọi dòng.COUNT(NULL)luôn ra 0 vì không dòng nào có giá trị [Đã chạy: MySQL 8.0.44]:
SELECT COUNT(*) AS star,
COUNT(1) AS one,
COUNT(NULL) AS just_null,
COUNT(note) AS note_col
FROM orders;
+------+-----+-----------+----------+
| star | one | just_null | note_col |
+------+-----+-----------+----------+
| 5 | 5 | 0 | 2 |
+------+-----+-----------+----------+
Không có lý do để thích COUNT(1) hơn COUNT(*) về kết quả. Tài liệu MySQL 8.0 nói rõ InnoDB xử lý COUNT(*) và COUNT(1) theo cùng một cách, không có chênh lệch hiệu năng.
- So sánh với lựa chọn khác. MySQL cho
COUNT(DISTINCT a, b)đếm số cặp khác nhau, và bỏ qua mọi cặp có một thành phần làNULL[Đã chạy: MySQL 8.0.44]:
SELECT COUNT(DISTINCT customer_id, note) AS pairs
FROM orders;
+-------+
| pairs |
+-------+
| 2 |
+-------+
Trên PostgreSQL, cú pháp này không tồn tại [Đã chạy: PostgreSQL 16.15]:
SELECT COUNT(DISTINCT customer_id, note) FROM orders;
ERROR: function count(integer, character varying) does not exist
LINE 1: SELECT COUNT(DISTINCT customer_id, note) FROM orders;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
- Hiểu lầm nâng cao. Có người nghĩ mọi hàm tổng hợp đều trả
NULLkhi không có dòng nào.COUNTlà ngoại lệ: nó trả 0 [Đã chạy: MySQL 8.0.44]:
SELECT COUNT(*) AS rows_found, COUNT(note) AS notes_found
FROM orders
WHERE id > 99;
+------------+-------------+
| rows_found | notes_found |
+------------+-------------+
| 0 | 0 |
+------------+-------------+
Bài sau sẽ cho thấy SUM cư xử khác ở đúng tình huống này.
- Giới hạn của bài. Bài này chưa nói đến
HAVINGđể lọc nhóm theo số đếm, và chưa nói về việcCOUNTtrên bảng lớn chạy nhanh hay chậm. - Đọc thêm. MySQL 8.0 Reference Manual, trang "Aggregate Function Descriptions" (phần
COUNT): https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html
Bài tiếp theo
Bài sau là hàm SUM(): cộng các con số của cột thay vì đếm dòng. Mini-dự án nối tiếp: tính doanh thu cuối ngày của cửa hàng shop từ cột total.
Lời giải và đáp án
Làm cùng mình, Bước 2. Hai biểu thức cần điền:
COUNT(note) AS with_note,
COUNT(DISTINCT customer_id) AS buyers
Câu đầy đủ [Đã chạy: MySQL 8.0.44]:
SELECT COUNT(*) AS all_orders,
COUNT(note) AS with_note,
COUNT(DISTINCT customer_id) AS buyers
FROM orders;
+------------+-----------+--------+
| all_orders | with_note | buyers |
+------------+-----------+--------+
| 5 | 2 | 3 |
+------------+-----------+--------+
Bước 3, ba cách đếm số đơn không có ghi chú:
SELECT COUNT(*) - COUNT(note) AS without_a,
SUM(CASE WHEN note IS NULL THEN 1 ELSE 0 END) AS without_b
FROM orders;
SELECT COUNT(*) AS without_c FROM orders WHERE note IS NULL;
+-----------+-----------+
| without_a | without_b |
+-----------+-----------+
| 3 | 3 |
+-----------+-----------+
+-----------+
| without_c |
+-----------+
| 3 |
+-----------+
Cả ba cách ra 3. Cách trừ là COUNT(*) - COUNT(note), cách WHERE đếm thẳng các dòng NULL. Bước 4: COUNT(note) bỏ qua đơn có note là NULL, nên nó chỉ đếm ghi chú thật chứ không đếm đơn.
Bài 1.
SELECT COUNT(*) AS a_rows,
COUNT(note) AS b_note,
COUNT(DISTINCT note) AS c_distinct_note,
COUNT(DISTINCT customer_id) AS d_buyers
FROM orders;
+--------+--------+-----------------+----------+
| a_rows | b_note | c_distinct_note | d_buyers |
+--------+--------+-----------------+----------+
| 6 | 4 | 3 | 4 |
+--------+--------+-----------------+----------+
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
COUNT(*) ra 6 | Đếm cả 6 dòng | Dấu sao đếm mọi dòng, không xét note |
COUNT(note) ra 4 | Bỏ hai dòng NULL (id 3 và 5) nhưng giữ dòng chuỗi rỗng (id 6) | Chuỗi rỗng là giá trị thật |
COUNT(DISTINCT note) ra 3 | Ba ghi chú khác nhau: gift wrap, fast delivery, chuỗi rỗng | Hai dòng gift wrap chỉ tính một lần |
COUNT(DISTINCT customer_id) ra 4 | Bốn khách khác nhau: 20, 21, 22, 23 | Khách 20 và 22 mỗi người xuất hiện hai lần |
Bài 2.
SELECT COUNT(*) AS big_orders,
COUNT(DISTINCT customer_id) AS big_buyers
FROM orders
WHERE total >= 100000;
+------------+------------+
| big_orders | big_buyers |
+------------+------------+
| 3 | 2 |
+------------+------------+
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
WHERE total >= 100000 | Giữ các đơn 1, 3, 5 | Lọc trước khi đếm |
COUNT(*) ra 3 | Đếm ba đơn còn lại | Đây là số đơn lớn |
COUNT(DISTINCT customer_id) ra 2 | Đơn 1 và 5 cùng của khách 10, đơn 3 của khách 11 | Khách 10 chỉ được đếm một lần |
Bài 3.
SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY c.id;
+------+-------------+
| name | order_count |
+------+-------------+
| An | 3 |
| Binh | 1 |
| Chi | 1 |
| Dung | 0 |
+------+-------------+
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
LEFT JOIN orders o ON o.customer_id = c.id | Giữ mọi khách, kể cả khách chưa có đơn | Cột của orders là NULL với khách như Dung |
COUNT(o.id) | Đếm các o.id khác NULL | Dòng của Dung có o.id là NULL nên không được đếm |
GROUP BY c.id, c.name | Một dòng kết quả cho mỗi khách | COUNT được tính riêng trong từng nhóm |
Tự kiểm tra.
COUNT(*)đếm dòng,COUNT(cột)đếm dòng có cột khácNULL,COUNT(DISTINCT cột)đếm số giá trị khác nhau và cũng bỏNULL.COUNT(*)là 4,COUNT(x)là 3,COUNT(DISTINCT x)là 2 (hai giá trị 1 và 2).-
COUNTkhông trả vềNULL, kể cả khi không có dòng nào.
WHEREloại dòng trước,COUNTchỉ đếm những dòng còn lại. Ví dụWHERE total >= 100000đưa số đơn từ 5 xuống 3.- Lỗi 1140, vì trộn cột thường với hàm tổng hợp mà không có
GROUP BY. Sửa bằngSELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id.