Cách chọn first-child trong mối quan hệ one-many trong database postgresql
Như mọi khi, trước khi đi sâu hơn vào mặt kỹ thuật, tôi cần phải miêu tả vấn đề tôi đang gặp phải. Đây là database diagram.
Tôi có hai table products và product_variants. Một product sẽ có nhiều product_variants. Các product_variants được sắp xếp theo thứ tự display_order.
Tôi muốn khai thác một số field của product và một số field của product_variants đầu tiên (first-child).
Table products (Đã tối giản)
| id | name |
|---|---|
| 1 | Vàng miếng SJC |
| 2 | Vàng nhẫn DOJI Quý-Hưng-Thịnh-Vượng-Vinh |
Table product_variants(Đã tối giản)
| id | product_id | name | price | display_order |
|---|---|---|---|---|
| 1 | 1 | miếng 10 chỉ | 150,000,000 VND | 1 |
| 2 | 1 | miếng 5 chỉ | 75,000,000 VND | 2 |
| 3 | 1 | miếng 2 chỉ | 30,000,000 VND | 3 |
| 4 | 1 | miếng 1 chỉ | 15,000,000 VND | 4 |
| 5 | 2 | nhẫn Vinh - 10 chỉ | 140,000,000 | 1 |
| 6 | 2 | nhẫn Vượng - 5 chỉ | 70,000,000 | 2 |
| 7 | 2 | nhẫn Thịnh - 2 chỉ | 28,000,000 VND | 3 |
| 8 | 2 | nhẫn Hưng - 1 chỉ | 14,000,000 VND | 4 |
| 9 | 2 | nhẫn Quý - 0.5 chỉ | 7,000,000 VND | 5 |
Và tôi muốn sau khi query sẽ có kết quả như sau, bạn nhìn kỹ nhé, tôi chỉ lấy product_variant có display_order nhỏ nhất:
| product_id | product_name | product_variant_id | product_variant_name | product_variant_price |
|---|---|---|---|---|
| 1 | Vàng miếng SJC | 1 | miếng 10 chỉ | 150,000,000 |
| 2 | Vàng nhẫn DOJI Quý-Hưng-Thịnh-Vượng-Vinh | 5 | nhẫn Vinh - 10 chỉ | 140,000,000 |
Câu hỏi đã là rõ ràng, bây giờ là câu query, lưu ý là bạn sẽ cần phải sử dụng distinct on và order.
ordernhằm giúp sắp xếp dữ liệu theodisplay orderdistinct onlà để loại trừ dữ liệu trùng lặpproduct_id- Các bươc sẽ như sau:
- [1] Chắc chắn là phải có
left join - [2] Sắp xếp theo thứ tự
product_id asc&display_order asc - [3] Dùng
distinct on (product_id)để chỉ giữ lại row đầu tiên của mỗiproduct_id
- [1] Chắc chắn là phải có
select distinct on (p.id)
p.id as product_id, p.name as product_name,
pv.id as product_variant_id, pv.name as product_variant_name, pv.price as product_variant_price
from products p
left join product_variants pv on p.id = pv.product_id
order by p.id asc, pv.display_order asc;
Lưu ý, đây không phải là cách query nhanh nhất. Tôi thấy có sequence scan trong Explain Execution Plan. Hiện tại thì data của tôi vẫn chưa
đủ lớn để tối ưu hóa. Nó nên dành cho bài post khác chứ không phải là ngay bây giờ.
Đây không phải là cách nhanh nhất. Tuy nhiên, đây chắc chắn là câu query dễ chịu nhất để viết và đọc.