[DATABASE] Phép chia trong SQL - Bài toán "tất cả"

- Thông thường, bài toán về phép chia có chứa từ khóa "tất cả" trong yêu cầu. Ví dụ: Tìm hóa đơn mua tất cả mặt hàng do Trung Quốc sản xuất; Cho biết sinh viên đăng kí tất cả các môn học,..."

- Khác với ngôn ngữ đại số quan hệ, ngôn ngữ SQL không hỗ trợ phép chia vì thế chúng ta phải thể hiện nó thông qua các toán tử: except, not exists, group by + having,...

Xét cơ sở dữ liệu quan hệ sau:

PHONGCHIEU (MaPC, TenPhong, SucChua, LoaiCN)

PHIM (MaPhim, TenPhim, ThoiLuong, NgayPhatHanh, DoanhThu)

THELOAI (MaTL, TenTL)

PHIM_THELOAI (MaPhim, MaTL)

SUATCHIEU (MaSC, MaPhim, NgayChieu, GioChieu, MaPC, TongTienVe)

--------------------

Cho biết những phim thuộc tất cả các thể loại?

-------------------


Cách 1: Thể hiện phép chia với NOT EXISTS - NOT EXISTS
Tạm dịch đề bài: Cho biết những Phim mà không có thể loại nào mà nó không thuộc vào?
Như vậy, sẽ liên quan tới 3 bảng PHIM, THE_LOAI và PHIM_THELOAI

+ Trả về bộ giá trị của bảng Phim nếu cả 2 điều kiện sau where cùng đúng 
+ Bộ giá trị của câu truy vấn con trả về chính là những phim mà chưa có thể loại, nếu câu truy vấn con không trả về bộ giá trị nào thì có nghĩa là phim thuộc tất cả các thể loại

- EXISTS: trả về tất cả bản ghi nếu có ít nhất 1 bộ trong truy vấn con => có ít nhất 1 bộ mà nó thuộc vào (phim thuộc ít nhất 1 bản ghi trong thể loại) 
- NOT EXISTS: trả về tất cả bản ghi nếu không có bộ nào trong truy vấn con => không có bộ nào mà nó không thuộc vào -> tất cả (phim thuộc tất cả bản ghi của thể loại)

/*** Giải thích: ***/
/*
- B1: Tìm danh sách thể loại không có trong PHIM_THELOAI
-> tức là thể loại đó chưa được làm phim
=> Dạng phép trừ sử dụng NOT EXIST
*/
 select *
 from THELOAI tl
 where NOT EXISTS (
                    select *
                    from PHIM_THELOAI p_tl
                    where tl.MaTL = p_tl.MaTL
                   )
-- Những phim thuộc tất cả thể loại không nằm trong danh sách này
/*
B2: Tìm danh sách phim không nằm trong danh sách đã trả về
- EXISTS: trả về tất cả bản ghi nếu có ít nhất 1 bộ trong truy vấn con
=> có ít nhất 1 bộ mà nó thuộc vào
(phim thuộc ít nhất 1 bản ghi trong thể loại)

- NOT EXISTS: trả về tất cả bản ghi nếu không có bộ nào trong truy vấn con
=> không có bộ nào mà nó không thuộc vào -> tất cả
(phim thuộc tất cả bản ghi của thể loại)
*/


Cách 2: Thể hiện phép chia với NOT EXISTS - EXCEPT

select *
from PHIM p
where NOT EXISTS (
                 (select tl.MaTL
                  from THELOAI tl)
            except
                 (select p_tl.MaTL
                  from PHIM_THELOAI p_tl
                  where p.MaPhim = p_tl.MaPhim)                                           
                );


Cách 3: Thể hiện phép chia với GROUP BY + HAVING
Nếu không xét về hiệu suất thì cách tiếp cận này là đơn giản, dễ hiểu nhất!


-- B1: Đếm số thể loại
select count(MaTL) from THELOAI;

-- B2: Đếm MaTL theo MaPhim
select MaPhim
from PHIM_THELOAI
group by MaPhim
having COUNT(MaTL) >= all (
                            select count(MaTL)
                            from THELOAI
                            );

-- B3: Hiển thị đầy đủ thông tin của Phim
select *
from PHIM
where MaPhim IN (select MaPhim
                 from PHIM_THELOAI
                 group by MaPhim
                 having COUNT(MaTL) >= all
                        (
                          select count(MaTL)
                          from THELOAI
                         )
                );



---
Link tải file SQL để import vào SQL server:

Nhận xét

Bài đăng phổ biến từ blog này

[DATABASE] Tìm mọi khóa của lược đồ quan hệ

[DATABASE] Phủ tối thiểu của tập phụ thuộc hàm

[DATABASE] Dạng chuẩn cao nhất của lược đồ quan hệ