CodeGym /Các khóa học /SQL SELF /Kiểm tra sự tồn tại dữ liệu với EXISTS và NOT EXISTS

Kiểm tra sự tồn tại dữ liệu với EXISTS và NOT EXISTS

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

Chào mừng bạn đến với bài giảng SQL mới! Hôm nay tụi mình sẽ làm quen với những toán tử tưởng như "vô hình" nhưng cực kỳ mạnh mẽ — EXISTS và NOT EXISTS. Hãy tưởng tượng bạn là một điệp viên, không để lại dấu vết nhưng luôn báo ngay: "Ừ, có đối tượng này" hoặc "Không, ở đây trống trơn". Hai toán tử này không trả về dữ liệu trực tiếp, mà giúp bạn thực hiện kiểm tra logic cực kỳ chính xác trong truy vấn.

Bắt đầu từ cơ bản nhé. EXISTS — là toán tử kiểm tra sự tồn tại của bản ghi trong kết quả của subquery. Nếu subquery trả về ít nhất một dòng, điều kiện EXISTS sẽ trả về TRUE, ngược lại là FALSE.

SELECT 1
WHERE EXISTS (
    SELECT * 
    FROM students 
    WHERE grade > 3.5
);

Như bạn thấy, mình không quan tâm dữ liệu trong subquery, chỉ cần biết có dòng nào thỏa điều kiện không thôi. Nếu có ít nhất một bản ghi phù hợp, truy vấn trả về 1.

Cú pháp EXISTS

Cú pháp EXISTS khá đơn giản:

SELECT cột
FROM bảng
WHERE EXISTS (
    SELECT 1
    FROM bảng_khác
    WHERE điều_kiện
);

Giải thích:

  • Subquery lồng trong EXISTS có thể là bất kỳ truy vấn nào.
  • Kết quả của subquery quyết định trả về TRUE hay FALSE.

Ví dụ: Có sinh viên nào điểm trên 4 không?

Giả sử có bảng students:

id name grade
1 Otto 3.2
2 Anna 4.7
3 Dan 5.0
4 Lina 2.9

Giả sử bạn muốn kiểm tra có sinh viên nào điểm trên 4 không. Dùng truy vấn sau:

SELECT 'Có sinh viên điểm cao!'
WHERE EXISTS (
    SELECT 1
    FROM students
    WHERE grade > 4
);

Kết quả:

Có sinh viên điểm cao!

Tại sao EXISTS chạy nhanh hơn IN?

Lợi thế lớn nhất của EXISTS là nó dừng thực thi subquery ngay khi tìm thấy kết quả đầu tiên. Nghĩa là nếu bạn chỉ cần kiểm tra sự tồn tại, EXISTS cực kỳ hiệu quả.

Ví dụ, bảng students có hàng triệu dòng, nhưng bạn chỉ cần biết có ai điểm > 4 không. Ngay khi SQL tìm thấy dòng đầu tiên thỏa điều kiện, truy vấn kết thúc luôn.

Sử dụng NOT EXISTS

Giờ nói về NOT EXISTS nhé. Toán tử này là "phản đề" của EXISTS. Nó trả về TRUE nếu subquery không trả về dòng nào.

Ví dụ: tìm sinh viên chưa có điểm (NULL)

Giả sử bảng có sinh viên chưa có điểm:

id name grade
1 Otto NULL
2 Anna 4.7
3 Dan 5.0
4 Lina NULL

Bạn muốn chọn tất cả sinh viên chưa có điểm. Dùng NOT EXISTS:

SELECT *
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM students 
    WHERE grade IS NOT NULL
    AND id = s.id
);

Kết quả:

id name grade
1 Otto NULL
4 Lina NULL

So sánh EXISTS và IN

Đôi khi bạn nghĩ EXISTS và IN giống nhau. Nhìn sơ thì đúng, nhưng có chi tiết nhỏ. Đặc biệt là khi có NULL xuất hiện. Lúc đó IN có thể hành xử rất "kỳ lạ", còn EXISTS lại cứu nguy.

Xem ví dụ nhé.

Bảng courses (các khóa học):

course_id name
1 Toán học
2 Lịch sử

Và đây là sinh viên:

student_id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

Bảng enrollments (ai đăng ký khóa nào):

student_id course_id
1 1
2 2
3 NULL

Bạn muốn chọn tên các khóa học có ai đó đăng ký. Nghe đơn giản nhỉ.

Dùng IN:

SELECT name
FROM courses
WHERE course_id IN (
    SELECT course_id 
    FROM enrollments
);

Nhìn qua tưởng sẽ chạy ngon. Nhưng nếu trong enrollments có NULL ở courseid, như Maria Chi, IN có thể trả về... chẳng gì cả! Vì NULL làm subquery "không xác định", SQL bối rối: biết đâu NULL lại chính là courseid mình cần?

Dùng EXISTS:

SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);

Còn EXISTS chỉ kiểm tra: "Có dòng nào course_id trùng không?" — thế là xong. Nó không ngại NULL, vì chỉ tìm đúng giá trị trùng thôi, không cần danh sách giá trị.

Kết luận: nếu subquery có thể có NULL, nên dùng EXISTS để tránh bất ngờ.

Ví dụ thực tế

Bảng students:

id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

Bảng enrollments:

student_id course_id
1 1
2 2
3 NULL

Ví dụ 1. Sinh viên đã đăng ký khóa học

Tìm những ai đã từng đăng ký khóa học (dù hơi lạ như Maria Chi):

SELECT name
FROM students s
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

Kết quả:

Alex Lin
Anna Song
Maria Chi

Chỉ cần sinh viên xuất hiện trong enrollments — là lọt vào kết quả, kể cả course_id của họ là NULL.

Ví dụ 2. Sinh viên chưa đăng ký khóa học

Giờ tìm những ai chỉ "tồn tại" trong hệ thống — chưa đăng ký gì cả:

SELECT name
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

Kết quả:

Dan Seth
Shadow Moon

Có vẻ hai bạn này chưa tìm được khóa học ưng ý. Hoặc quên đăng ký rồi :)

Ví dụ 3. Chọn khóa học có hơn 5 sinh viên đăng ký

Bảng courses:

course_id name
1 Toán học
2 Lịch sử
3 Sinh học
4 Triết học

Bảng enrollments:

student_id course_id
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 NULL

Bạn muốn tìm các khóa học có hơn năm sinh viên đăng ký. Ở đây EXISTS như hỏi: "Có nhóm nào của khóa này có hơn 5 sinh viên không?"

SELECT name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE c.course_id = e.course_id
    GROUP BY e.course_id
    HAVING COUNT(*) > 5
);

Kết quả:

Toán học

Chỉ có khóa "Toán học" (course_id = 1) có sáu sinh viên đăng ký. Các khóa khác chưa hot lắm.

Lỗi thường gặp khi dùng EXISTS và NOT EXISTS

  1. Hiểu sai cú pháp subquery. Luôn kiểm tra subquery có tham chiếu đúng bảng ngoài không nhé.
  2. Bỏ sót kiểm tra NULL. Dù dùng EXISTS, đôi khi bạn vẫn cần xử lý NULL rõ ràng.
  3. Không có index trên các trường subquery. Điều này có thể làm truy vấn chạy chậm hẳn.

Vậy là hết cho hôm nay! Giờ bạn đã biết cách dùng EXISTS và NOT EXISTS để kiểm tra sự tồn tại dữ liệu, cũng như sự khác biệt giữa chúng với IN. Bài sau tụi mình sẽ tiếp tục khám phá subquery, xem cách dùng trong SELECT để làm việc với dữ liệu tổng hợp nhé.

2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Tìm kiếm sinh viên không có điểm số
Tìm kiếm sinh viên không có điểm số
2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Kiểm tra các khóa học có sinh viên đã đăng ký
Kiểm tra các khóa học có sinh viên đã đăng ký
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION