CodeGym /Các khóa học /SQL SELF /Sử dụng SELECT bên trong SELECT

Sử dụng SELECT bên trong SELECT

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

Giả sử bạn đang làm analyst ở trường đại học (tụi mình đang làm database cho trường mà, nhớ không?). Người ta nhờ bạn không chỉ liệt kê sinh viên và điểm số của họ, mà còn thêm một cột với điểm cao nhất trong nhóm để dễ so sánh kết quả. Làm sao giải quyết vụ này? Đương nhiên là dùng subquery trong SELECT rồi!

Subquery trong SELECT cho phép bạn tính giá trị ngay lúc query chính chạy. Cực tiện luôn, vì bạn có thể kết hợp tính toán aggregate, filter phức tạp và thậm chí lấy dữ liệu từ bảng khác trong cùng một query.

Cơ bản về nested query trong SELECT

Subquery trong SELECT đúng như tên gọi: bạn nhét kết quả của một SELECT vào trong cái khác. Nhờ vậy, bạn có thể tính thêm giá trị cho từng dòng kết quả.

Ví dụ đơn giản nè. Giả sử có bảng students với cấu trúc như sau:

student_id name group_id
1 Linda 101
2 Otto 102
3 Anna 101

Và bảng grades:

grade_id student_id grade
1 1 5
2 1 4
3 2 3
4 3 5
5 3 4

Ví dụ 1: Thêm điểm cao nhất trong nhóm

Bài toán: liệt kê tên sinh viên, điểm số của họ và điểm cao nhất trong nhóm, để thấy điểm mỗi người chênh lệch thế nào so với top của nhóm.

SQL code:

SELECT
    s.name AS student_name,
    g.grade AS student_grade,
    (
        SELECT MAX(grade) -- query này trả về đúng một giá trị duy nhất 
        FROM grades 
        INNER JOIN students ON grades.student_id = students.student_id
        WHERE students.group_id = s.group_id
    ) AS max_group_grade 
FROM 
    students s
INNER JOIN 
    grades g ON s.student_id = g.student_id;

Ở đây có gì:

  1. Với mỗi sinh viên, mình lấy tên và điểm (s.name, g.grade).
  2. SELECT MAX(grade) — đây là subquery trả về điểm cao nhất trong nhóm của sinh viên đó.
  3. Subquery chạy cho từng dòng của query chính và dùng điều kiện WHERE students.group_id = s.group_id để chỉ lấy trong một nhóm.

Ví dụ 2: Điểm trung bình của nhóm

Muốn hữu ích hơn cho analyst? Thêm vào kết quả không chỉ điểm cao nhất mà còn cả điểm trung bình của nhóm luôn.

SQL code:

SELECT
    s.name AS student_name,
    g.grade AS student_grade,
    (
        SELECT AVG(grade) 
        FROM grades 
        INNER JOIN students ON grades.student_id = students.student_id
        WHERE students.group_id = s.group_id
    ) AS avg_group_grade
FROM 
    students s
INNER JOIN 
    grades g ON s.student_id = g.student_id;

Bây giờ:

  • Thay vì MAX() thì dùng AVG() để tính điểm trung bình của nhóm.
  • Có phân tích dữ liệu "real-time" luôn.

Giới hạn và khuyến nghị

Subquery trong SELECT mạnh thật, nhưng dùng phải cẩn thận nha:

  1. Hiệu năng. Mỗi subquery sẽ chạy cho từng dòng của query chính. Nếu bảng to thì query sẽ chậm. Ví dụ bảng có 1000 sinh viên thì subquery chạy 1000 lần luôn!
  2. Index. Để query chạy nhanh, nhớ tạo index đúng cho các cột dùng trong điều kiện WHERE của subquery.
  3. Dễ đọc. Đừng lồng quá nhiều subquery. Nếu thấy phức tạp quá thì chuyển sang FROM hoặc tạo bảng tạm cũng được.

Ví dụ sử dụng

Cùng xem thêm vài case thú vị nữa nha.

Ví dụ 3: Số lượng khoá học của mỗi sinh viên

Liệt kê bảng, mỗi sinh viên sẽ hiện số khoá học mà họ đã đăng ký. Bảng enrollments liên kết với sinh viên qua student_id:

student_id course_id
1 201
1 202
2 201
3 203

SQL code:

SELECT
    s.name AS student_name,
    (
        SELECT COUNT(*) 
        FROM enrollments
        WHERE enrollments.student_id = s.student_id
    ) AS course_count
FROM 
    students s;

Ở đây subquery sẽ đếm số dòng trong bảng enrollments cho từng sinh viên.

Ví dụ 4: Flag "học sinh xuất sắc" cho từng sinh viên

Hiển thị xem sinh viên có phải là học sinh xuất sắc không. Giả sử tiêu chí là tất cả điểm của họ đều bằng 5.

SQL code:

SELECT
    s.name AS student_name,
    (
        SELECT CASE 
            WHEN MIN(g.grade) = 5 THEN 'Học sinh xuất sắc'
            ELSE 'Không xuất sắc'
        END
        FROM grades g
        WHERE g.student_id = s.student_id
    ) AS status
FROM 
    students s;

Ở đây dùng CASE lồng để gán status "Học sinh xuất sắc" chỉ cho những ai tất cả điểm đều là 5.

Tối ưu subquery trong SELECT

Đã nói rồi, hiệu năng có thể là vấn đề. Đây là vài tips để cải thiện:

  1. Dùng index. Nếu subquery filter dữ liệu, nhớ đảm bảo các cột đó có index.
  2. Cache kết quả. Đôi khi nên chuyển subquery thành view (VIEW) hoặc bảng tạm.
  3. Giảm lồng ghép. Đừng lồng quá nhiều nếu có cách đơn giản hơn.

Subquery trong SELECT mở ra rất nhiều khả năng tính toán và phân tích dữ liệu. Dù có thể tốn tài nguyên, nếu tối ưu đúng thì SQL sẽ cực kỳ mạnh mẽ và linh hoạt. Đừng ngại thử nghiệm và tìm cách nâng cấp query của bạn nhé!

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