Hôm nay tụi mình sẽ tìm hiểu cách tận dụng tối đa mảng trong truy vấn: gom giá trị thành nhóm, lọc theo nội dung và thậm chí sắp xếp ngay trong mảng luôn. Không phải lý thuyết suông đâu — mấy chiêu này gặp hoài trong báo cáo, phân tích, cá nhân hóa và nhiều tình huống thực tế khác. Hiểu nguyên lý là dễ thôi — và giờ tụi mình sẽ làm rõ luôn.
Tổng hợp dữ liệu với mảng
Làm việc với mảng cực kỳ hữu ích khi cần nhóm dữ liệu. Thay vì lấy nhiều dòng — mình gom giá trị cần thiết vào một mảng gọn gàng. Phân tích dễ hơn, kết quả gọn hơn và thường khỏi cần subquery lằng nhằng. Xem thử thực tế nó chạy ra sao nhé.
Ví dụ 1: nhóm dữ liệu thành mảng với array_agg()
Khi bạn muốn gom giá trị từ nhiều dòng trong một nhóm thành mảng, array_agg() là cứu tinh. Đây chắc là function hữu dụng nhất khi xử lý mảng lúc tổng hợp.
-- Mình có bảng students với các trường id, name và course
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
course VARCHAR(100)
);
-- Thêm vài dòng dữ liệu
INSERT INTO students (name, course) VALUES
('Alysa', 'Toán học'),
('Bob', 'Toán học'),
('Charlie', 'Vật lý'),
('Dave', 'Vật lý'),
('Emma', 'Toán học');
-- Nhóm sinh viên theo khóa học thành mảng
SELECT course, array_agg(name) AS students
FROM students
GROUP BY course;
Kết quả:
| course | students |
|---|---|
| Toán học | {Alysa, Bob, Emma} |
| Vật lý | {Charlie, Dave} |
Nhóm giá trị thành mảng rất tiện nếu bạn muốn trả về dữ liệu ở dạng dễ parse, ví dụ như JSON chẳng hạn.
Ví dụ 2: tạo mảng lồng nhau
Giả sử mình có thêm một bảng nữa và muốn gom dữ liệu từ hai bảng thành mảng? Ví dụ, bảng courses chứa thông tin giảng viên.
-- Tạo bảng giảng viên
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
teacher VARCHAR(100)
);
-- Thêm dữ liệu
INSERT INTO courses (name, teacher) VALUES
('Toán học', 'Prof. Min'),
('Vật lý', 'Prof. Peterson');
-- Truy vấn lồng nhau để tạo mảng
SELECT
c.name AS course_name,
array_agg(s.name) AS students,
c.teacher
FROM
courses c
LEFT JOIN
students s
ON
c.name = s.course
GROUP BY
c.name, c.teacher;
Kết quả:
| course_name | students | teacher |
|---|---|---|
| Toán học | {Alysa, Bob, Emma} | Prof. Min |
| Vật lý | {Charlie, Dave} | Prof. Peterson |
Giờ mình có bảng cực tiện, show khóa học, giảng viên và sinh viên dưới dạng mảng luôn.
Lọc dữ liệu với mảng
Mảng tự nó đã mạnh rồi, nhưng thực sự bá đạo khi biết lọc dữ liệu dựa trên mảng. Muốn chọn user có từ khóa cụ thể trong danh sách sở thích? Hay đơn hàng mà mỗi giá đều vượt ngưỡng nào đó? Tất cả làm được ngay trong SQL — khỏi cần code thêm ở app.
Ví dụ 1: lọc dòng theo phần tử trong mảng
Giả sử mình muốn tìm tất cả dòng mà mảng chứa giá trị cụ thể, ví dụ tìm sinh viên đăng ký khóa toán học.
-- Lọc sinh viên đăng ký khóa học bằng `ANY`
SELECT *
FROM students
WHERE course = ANY(ARRAY['Toán học', 'Vật lý']);
Ở đây ANY cho phép chỉ định mảng giá trị, và truy vấn trả về dòng mà course khớp ít nhất một giá trị trong mảng.
Ví dụ 2: Kiểm tra giao nhau giữa mảng
Giờ giả sử mình có bảng student_interests, ghi sở thích sinh viên dưới dạng mảng. Mình muốn tìm sinh viên có sở thích trùng với tiêu chí của mình.
-- Tạo bảng sở thích sinh viên
CREATE TABLE student_interests (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
interests TEXT[]
);
-- Thêm dữ liệu
INSERT INTO student_interests (name, interests) VALUES
('Alysa', ARRAY['lap trinh', 'am nhac']),
('Bob', ARRAY['the thao', 'lap trinh']),
('Charlie', ARRAY['doc sach', 'nhiep anh']),
('Emma', ARRAY['am nhac', 'the thao']);
-- Tìm sinh viên quan tâm lap trinh hoặc am nhac
SELECT *
FROM student_interests
WHERE interests && ARRAY['lap trinh', 'am nhac'];
Toán tử && kiểm tra giao nhau giữa hai mảng. Nếu có ít nhất một phần tử bên trái trùng với mảng bên phải, dòng đó được chọn.
Kết quả:
| id | name | interests |
|---|---|---|
| 1 | Alysa | {lap trinh, am nhac} |
| 2 | Bob | {the thao, lap trinh} |
| 4 | Emma | {am nhac, the thao} |
Sắp xếp mảng
Đôi khi thứ tự giá trị trong mảng rất quan trọng — nhất là khi gom mảng từ nhiều dòng hoặc muốn chuẩn bị dữ liệu để hiển thị. PostgreSQL cho phép sắp xếp phần tử ngay trong truy vấn, khỏi cần xử lý thêm.
Ví dụ 1: sắp xếp giá trị trong mảng
Đôi khi cần sắp xếp phần tử trong mảng. Ví dụ, sắp xếp sở thích sinh viên theo alphabet.
-- Sắp xếp phần tử mảng bằng function `array_sort()`
SELECT
name,
array_sort(interests) AS sorted_interests
FROM
student_interests;
Kết quả:
| name | sorted_interests |
|---|---|
| Alysa | {am nhac, lap trinh} |
| Bob | {lap trinh, the thao} |
| Charlie | {doc sach, nhiep anh} |
| Emma | {am nhac, the thao} |
Ví dụ 2: sắp xếp dòng theo độ dài mảng
Giờ giả sử mình muốn sắp xếp sinh viên theo số lượng sở thích — từ "đam mê" nhất đến "nhạt" nhất.
-- Sắp xếp dòng theo độ dài mảng
SELECT
name,
interests,
array_length(interests, 1) AS interests_count
FROM
student_interests
ORDER BY
interests_count DESC;
Kết quả:
| name | interests | interests_count |
|---|---|---|
| Alysa | {lap trinh, am nhac} | 2 |
| Bob | {the thao, lap trinh} | 2 |
| Charlie | {doc sach, nhiep anh} | 2 |
| Emma | {am nhac, the thao} | 2 |
Dù ví dụ này ai cũng có số sở thích bằng nhau, truy vấn kiểu này có thể chỉnh lại cho bảng lớn hơn.
GO TO FULL VERSION