Unpivot: Chuyển cột môn học thành hàng (cột đơn) với UNION ALL trong MySQL
Khi dữ liệu lưu theo định dạng rộng (wide format: mỗi môn một cột), dùng UNION ALL để chuyển sang định dạng dài (long format: một cột SubjectName, một cột Score) để dễ báo cáo, phân tích.
Ví dụ minh họa
Tạo bảng DemoTable729 (định dạng rộng):
mysql> CREATE TABLE DemoTable729 (
StudentId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
StudentName VARCHAR(100),
MySQLMarks INT,
CMarks INT,
JavaMarks INT
);
Query OK, 0 rows affected (0.40 sec)
Chèn dữ liệu:
mysql> INSERT INTO DemoTable729(StudentName,MySQLMarks,CMarks,JavaMarks) VALUES('Chris',94,67,75);
Query OK, 1 row affected (0.21 sec)
mysql> INSERT INTO DemoTable729(StudentName,MySQLMarks,CMarks,JavaMarks) VALUES('Robert',45,99,54);
Query OK, 1 row affected (0.18 sec)
mysql> INSERT INTO DemoTable729(StudentName,MySQLMarks,CMarks,JavaMarks) VALUES('David',57,89,43);
Query OK, 1 row affected (0.12 sec)
Xem dữ liệu gốc (3 hàng, 5 cột):
mysql> SELECT * FROM DemoTable729;
+-----------+-------------+------------+--------+-----------+ | StudentId | StudentName | MySQLMarks | CMarks | JavaMarks | +-----------+-------------+------------+--------+-----------+ | 1 | Chris | 94 | 67 | 75 | | 2 | Robert | 45 | 99 | 54 | | 3 | David | 57 | 89 | 43 | +-----------+-------------+------------+--------+-----------+ 3 rows in set (0.00 sec)
Chuyển sang định dạng dài (9 hàng, 4 cột) với UNION ALL:
mysql> SELECT StudentId, StudentName, 'MySQL' AS SubjectName, MySQLMarks AS Score FROM DemoTable729 UNION ALL SELECT StudentId, StudentName, 'C' AS SubjectName, CMarks AS Score FROM DemoTable729 UNION ALL SELECT StudentId, StudentName, 'Java' AS SubjectName, JavaMarks AS Score FROM DemoTable729;
Kết quả:
+-----------+-------------+-------------+-------+ | StudentId | StudentName | SubjectName | Score | +-----------+-------------+-------------+-------+ | 1 | Chris | MySQL | 94 | | 2 | Robert | MySQL | 45 | | 3 | David | MySQL | 57 | | 1 | Chris | C | 67 | | 2 | Robert | C | 99 | | 3 | David | C | 89 | | 1 | Chris | Java | 75 | | 2 | Robert | Java | 54 | | 3 | David | Java | 43 | +-----------+-------------+-------------+-------+ 9 rows in set (0.00 sec)
UNION vs UNION ALL
| Toán tử | Xử lý trùng lặp | Hiệu suất | Khi nào dùng |
|---|---|---|---|
| UNION ALL | Giữ tất cả (kहông distinct) | Nhanh hơn | Biết chắc không trùng / muốn giữ trùng |
| UNION | Loại bỏ trùng (DISTINCT) | Chậm hơn (sort/hash) | Cần kết quả duy nhất |
Ở đây dùng UNION ALL vì mỗi SELECT trả về SubjectName khác nhau → không bao giờ trùng.
Ứng dụng thực tế
- Báo cáo điểm theo môn, học kỳ
- Chuẩn bị dữ liệu cho BI tool (Power BI, Tableau) - thích định dạng dài
- Pivot ngược lại (long → wide): dùng
CASE WHEN+GROUP BYhoặcPIVOT(MySQL không có PIVOT native)
MySQL 8.0+: CTE làm code sạch hơn
WITH unpivoted AS (
SELECT StudentId, StudentName, 'MySQL' AS Subject, MySQLMarks AS Score FROM DemoTable729
UNION ALL SELECT StudentId, StudentName, 'C', CMarks FROM DemoTable729
UNION ALL SELECT StudentId, StudentName, 'Java', JavaMarks FROM DemoTable729
)
SELECT * FROM unpivoted ORDER BY StudentId, Subject;