Bây giờ là lúc nâng trình và tạo một trong những kiểu quan hệ hay gặp nhất trong cơ sở dữ liệu quan hệ — quan hệ ONE-TO-MANY.
Hãy tưởng tượng một công ty nhỏ. Một nhân viên chỉ làm việc ở một phòng ban, nhưng một phòng ban có thể có cả chục nhân viên như vậy. Ở đây tụi mình có hai đối tượng thực tế: nhân viên và phòng ban. Quan hệ giữa chúng có thể mô tả là "một phòng ban có thể chứa nhiều nhân viên", hay nói kiểu formal là "một-nhiều" (ONE-TO-MANY).
Kiểu quan hệ ONE-TO-MANY này xuất hiện khắp nơi luôn:
- một khách hàng có thể đặt nhiều đơn hàng;
- một tác giả có thể viết nhiều cuốn sách;
- một giáo viên có thể dạy nhiều học sinh.
Trong cơ sở dữ liệu quan hệ, quan hệ ONE-TO-MANY được triển khai bằng cách dùng khóa ngoại (FOREIGN KEY). Một trong các cột của bảng "nhiều" (MANY) sẽ trỏ tới khóa chính của bảng "một" (ONE).
Làm sao tạo quan hệ ONE-TO-MANY
Cùng xem ví dụ kinh điển: quan hệ giữa khách hàng và đơn hàng. Một khách hàng có thể đặt nhiều đơn hàng, nhưng mỗi đơn hàng chỉ gắn với một khách hàng thôi. Tụi mình sẽ tạo hai bảng: customers (khách hàng) và orders (đơn hàng).
Bảng customers
Đây là bảng "một". Nó sẽ lưu thông tin về khách hàng.
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY, -- Định danh duy nhất của khách hàng
name TEXT NOT NULL -- Tên khách hàng
);
Bảng orders
Đây là bảng "nhiều". Nó lưu các đơn hàng, mỗi đơn sẽ có khóa ngoại customer_id trỏ tới customer_id của bảng customers.
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY, -- Định danh duy nhất của đơn hàng
order_date DATE NOT NULL, -- Ngày đặt hàng
customer_id INT REFERENCES customers(customer_id) -- Khóa ngoại
);
Ứng dụng thực tế
Chèn dữ liệu
Bây giờ tụi mình sẽ thêm một ít dữ liệu vào các bảng để test quan hệ này.
Thêm khách hàng vào bảng customers:
INSERT INTO customers (name)
VALUES
('Ada Lovelace'),
('Grace Hopper'),
('Linus Torvalds');
Kết quả:
| customer_id | name |
|---|---|
| 1 | Ada Lovelace |
| 2 | Grace Hopper |
| 3 | Linus Torvalds |
Thêm đơn hàng vào bảng orders:
INSERT INTO orders (order_date, customer_id)
VALUES
('2023-10-01', 1), -- Đơn của Ada
('2023-10-02', 2), -- Đơn của Grace
('2023-10-03', 1); -- Thêm một đơn nữa của Ada
Bảng orders:
| order_id | order_date | customer_id |
|---|---|---|
| 1 | 2023-10-01 | 1 |
| 2 | 2023-10-02 | 2 |
| 3 | 2023-10-03 | 1 |
Lưu ý: khi thêm đơn hàng nhớ phải chỉ định customer_id đã tồn tại nha. Nếu bạn thử nhập một ID không tồn tại, database sẽ báo lỗi liền. Đây là cách bảo vệ tính toàn vẹn dữ liệu.
Kiểm tra quan hệ
Giờ tụi mình thử xem hai bảng này liên kết thế nào. Ví dụ: Ada Lovelace đã đặt những đơn nào?
SELECT orders.order_id, orders.order_date, customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.customer_id
WHERE customers.name = 'Ada Lovelace';
Kết quả:
| order_id | order_date | name |
|---|---|---|
| 1 | 2023-10-01 | Ada Lovelace |
| 3 | 2023-10-03 | Ada Lovelace |
Ở đây tụi mình dùng lệnh JOIN để nối hai bảng dựa trên khóa ngoại. Rất tiện, đẹp — và không bị trùng dữ liệu!
Tại sao cần cái này
Quan hệ ONE-TO-MANY siêu phổ biến và hữu ích ngoài đời thực. Hãy tưởng tượng một shop online có hàng ngàn khách hàng và hàng triệu đơn hàng. Thay vì lặp lại thông tin khách hàng trong từng đơn, tụi mình lưu thông tin khách hàng duy nhất ở một bảng, còn đơn hàng ở bảng khác. Như vậy dữ liệu gọn hơn và database cũng dễ quản lý hơn.
Thêm nữa, việc liên kết dữ liệu cho phép tạo ra các truy vấn phân tích mạnh mẽ. Ví dụ, bạn có thể hỏi: "Mỗi khách hàng đã đặt bao nhiêu đơn?" hoặc "Khách hàng nào đã đặt đơn trong tháng vừa rồi?".
Khó khăn và bẫy thường gặp
Đây là mấy chỗ newbie hay bị vấp nè:
Thiếu khóa ngoại. Nếu bạn quên thêm khóa ngoại vào bảng "nhiều", thì quan hệ này chỉ tồn tại trong đầu bạn thôi, chứ database không kiểm soát được. Điều này có thể làm database bị "lỗi", ví dụ đơn hàng trỏ tới khách hàng không tồn tại.
Cố xóa bản ghi ở bảng "một". Ví dụ, nếu bạn xóa khách hàng khỏi customers, thì các đơn hàng của họ trong orders sẽ bị "treo". Để tránh chuyện này, bạn có thể dùng ON DELETE CASCADE để khi xóa khách hàng thì đơn hàng của họ cũng tự động bị xóa luôn.
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date DATE NOT NULL,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE
);
Bây giờ, nếu bạn xóa khách hàng:
DELETE FROM customers WHERE customer_id = 1;
Tất cả đơn hàng của họ cũng sẽ bị xóa. Database sẽ sạch như ly cà phê mới pha vậy.
Lỗi khi chèn dữ liệu. Nếu bạn thử chèn đơn hàng với customer_id không tồn tại, bạn sẽ gặp lỗi kiểu như:
ERROR: insert or update on table "orders" violates foreign key constraint
GO TO FULL VERSION