CodeGym /Các khóa học /SQL SELF /Sử dụng subquery trong FROM

Sử dụng subquery trong FROM

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

Cùng đào sâu hơn nữa vào "hang thỏ": tụi mình sẽ xem cách dùng subquery trong phần FROM. Đây là một trong những chiêu phổ biến nhất của dân code SQL, vì nó cho phép tạo ra những bảng tạm cực mạnh ngay "tại chỗ" và tái sử dụng như thể nó là bảng thật trong database luôn.

Giả sử bạn đang làm một báo cáo cần tính toán, group hay filter dữ liệu, nhưng lại không muốn tạo bảng tạm trên server. Làm sao đây? Lúc này subquery trong FROM chính là cứu tinh. Nó giúp bạn:

  • Tạm thời kết hợp hoặc tổng hợp dữ liệu trước khi chạy truy vấn chính.
  • Tạo ra các tập dữ liệu có cấu trúc "ngay tại chỗ".
  • Giảm số thao tác, yêu cầu database lưu trữ ít dữ liệu trung gian nhất có thể.

Subquery trong FROM hoạt động như những mini-table mà bạn có thể dùng trong truy vấn chính. Nó giống như lắp ráp mô-đun: nhanh, linh hoạt và không tốn nhiều tài nguyên :)

Cơ bản về subquery trong FROM

Với subquery trong FROM, tụi mình dùng subquery để tạo bảng tạm (hoặc sub-table), nó sẽ trở thành một phần của truy vấn tổng thể. Để làm được điều này, bạn cần ba bước chính:

  1. Viết subquery trong phần FROM và đặt nó trong dấu ngoặc tròn.
  2. Đặt alias (bí danh) cho subquery đó.
  3. Dùng alias này như thể nó là bảng thật.

Cú pháp

SELECT cột
FROM (
    SELECT cột
    FROM bảng
    WHERE điều_kiện
) AS bí_danh
WHERE điều_kiện_bên_ngoài;

Nghe có vẻ hơi căng nhỉ? Thôi chuyển qua ví dụ cho dễ hiểu nha.

Ví dụ: Sinh viên và điểm trung bình

Giả sử tụi mình có hai bảng:

students (dữ liệu sinh viên — tên và ID):

student_id student_name
1 Alex
2 Anna
3 Dan

grades (dữ liệu điểm của sinh viên):

grade_id student_id grade
1 1 80
2 1 85
3 2 90
4 3 70
5 3 75

Giờ nhiệm vụ là: lấy danh sách sinh viên và điểm trung bình của họ.

Tụi mình có thể bắt đầu bằng một subquery đơn giản tính điểm trung bình từng sinh viên, rồi dùng nó trong truy vấn chính.

SELECT s.student_name, g.avg_grade
FROM (
    SELECT student_id, AVG(grade) AS avg_grade
    FROM grades
    GROUP BY student_id
) AS g
JOIN students AS s ON s.student_id = g.student_id;

Kết quả:

student_name avg_grade
Alex 82.5
Anna 90.0
Dan 72.5

Bảng tạm "ngay tại chỗ"

Subquery trong FROM cực kỳ hữu ích khi bạn cần xử lý dữ liệu nhiều tầng. Ví dụ, nếu bạn muốn không chỉ lấy điểm trung bình mà còn tính điểm cao nhất của mỗi sinh viên — tất cả trong một truy vấn.

SELECT g.student_id, g.avg_grade, g.max_grade
FROM (
    SELECT student_id, 
           AVG(grade) AS avg_grade, 
           MAX(grade) AS max_grade
    FROM grades
    GROUP BY student_id
) AS g;

Kết quả:

student_id avg_grade max_grade
1 82.5 85
2 90 90
3 72.5 75

Lưu ý là nó hoạt động như một bảng tạm thực thụ với các cột riêng: avg_grade và max_grade.

Khi nào nên dùng subquery trong FROM?

Khi cần dữ liệu tổng hợp. Nếu bạn muốn tính toán trước (ví dụ: trung bình, tổng, max), rồi mới join với bảng khác.

Khi cần lọc dữ liệu. Khi bạn muốn lọc dữ liệu trước khi join với bảng chính.

Khi muốn đơn giản hóa truy vấn phức tạp. Chia nhỏ bài toán giúp code dễ hiểu hơn, đỡ rối não.

Ví dụ: Báo cáo sinh viên với hai tầng xử lý

Giả sử bạn muốn tìm sinh viên có điểm trung bình trên 80. Bắt đầu bằng subquery tính điểm trung bình, rồi dùng nó để filter.

SELECT s.student_name, g.avg_grade
FROM students AS s
JOIN (
    SELECT student_id, AVG(grade) AS avg_grade
    FROM grades
    GROUP BY student_id
) AS g ON s.student_id = g.student_id
WHERE g.avg_grade > 80;

Kết quả:

student_name avg_grade
Alex 82.5
Anna 90.0

Lưu ý khi dùng và khuyến nghị

Alias là bắt buộc. Luôn đặt alias cho subquery (ví dụ AS g), nếu không PostgreSQL sẽ không biết gọi "bảng tạm" đó thế nào đâu.

Tối ưu hóa. Subquery trong FROM đôi khi chạy chậm hơn join bảng (JOIN), nhất là khi bạn filter dữ liệu bên trong subquery.

Indexing. Đảm bảo các trường dùng để join, index và filter đã được tối ưu — cái này ảnh hưởng lớn đến hiệu năng luôn đó.

Ví dụ truy vấn phức tạp: khóa học và số lượng sinh viên

Giờ thử làm bài toán thực tế khó hơn chút. Giả sử bạn có bảng sau:

courses (danh sách khóa học):

course_id course_name
1 SQL Basics
2 Python Basics

Và enrollments (danh sách sinh viên đăng ký khóa học):

student_id course_id
1 1
1 2
2 1

Bây giờ tụi mình muốn biết mỗi khóa học có bao nhiêu sinh viên đăng ký.

SELECT c.course_name, e.students_count
FROM courses AS c
JOIN (
    SELECT course_id, COUNT(student_id) AS students_count
    FROM enrollments
    GROUP BY course_id
) AS e ON c.course_id = e.course_id;

Kết quả:

course_name students_count
SQL Basics 2
Python Basics 1

Hy vọng bài giảng này bạn thấy hay... nhưng bài sau còn thú vị hơn nữa đó :)

2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Đếm số lượng sinh viên có điểm số
Đếm số lượng sinh viên có điểm số
2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Số lượng sinh viên trên mỗi khóa học
Số lượng sinh viên trên mỗi khóa học
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION