Trino SQL Bài học 33: Cấu trúc dữ liệu dạng Từ điển (MAP) (Key-Value)
Mục tiêu bài học:
-
Hiểu bản chất lưu trữ cặp Khóa - Giá trị (Key-Value) của kiểu dữ liệu
MAP. -
So sánh sự khác biệt về tính "Kiểu tĩnh" (Strongly Typed) giữa
MAPcủa Trino và Dictionary của Python. -
Khởi tạo mảng từ điển và trích xuất dữ liệu một cách an toàn.
-
Kết hợp các hàm
map_keys,map_valuesvàUNNESTđể khai phá dữ liệu bên trong cấu trúcMAP.
1. Bản chất của kiểu dữ liệu MAP
Nếu ARRAY (Bài 30) là một danh sách các giá trị được đánh số thứ tự từ 1 đến N, thì MAP (Từ điển) là một tập hợp các cặp Khóa - Giá trị (Key - Value). Thay vì gọi phần tử thứ nhất, thứ hai, bạn sẽ gọi phần tử bằng Tên Khóa (Key). Cấu trúc này cực kỳ phổ biến khi lưu trữ thuộc tính sản phẩm (Properties) hoặc thông số cấu hình hệ thống.
Đặc tính "Kiểu tĩnh" khắc nghiệt của Trino: Giống như Python Dictionary, mỗi Khóa (Key) trong một MAP phải là duy nhất. Tuy nhiên, khác với Python (bạn có thể lưu chuỗi, số lộn xộn trong cùng 1 dictionary), Trino yêu cầu tất cả các Khóa phải cùng một kiểu, và tất cả các Giá trị phải cùng một kiểu.
-
Cấu trúc chuẩn:
MAP(Kiểu_của_Key, Kiểu_của_Value). -
Ví dụ:
MAP(VARCHAR, INTEGER)(Khóa là chuỗi, Giá trị là số nguyên).
2. Khởi tạo MAP (Initialization)
Để tạo ra một dữ liệu MAP trong Trino, cách phổ biến nhất là sử dụng hàm MAP() kết hợp với 2 mảng (ARRAY): Mảng thứ nhất chứa danh sách các Khóa, mảng thứ hai chứa danh sách các Giá trị tương ứng.
SQL
SELECT
'Laptop Dell' AS product_name,
-- Khởi tạo MAP(VARCHAR, VARCHAR)
MAP(
ARRAY['cpu', 'ram', 'storage'],
ARRAY['Core i7', '16GB', '512GB SSD']
) AS specs;
Lưu ý: Hai mảng truyền vào hàm MAP() bắt buộc phải có số lượng phần tử bằng nhau, nếu không Trino sẽ báo lỗi.
3. Truy xuất dữ liệu: Cạm bẫy "Key not present"
Làm thế nào để lấy ra dung lượng RAM từ cột specs ở ví dụ trên?
Cách 1: Dùng ngoặc vuông [key] (Rất nguy hiểm)
SQL
SELECT specs['ram'] AS ram_capacity FROM table;
Cú pháp này rất ngắn gọn, nhưng nó mang rủi ro "Fail-fast" y hệt như Index của ARRAY. Nếu bạn truy vấn specs['vga'] nhưng trong CSDL có dòng không chứa key 'vga', Trino sẽ đánh sập toàn bộ câu truy vấn với lỗi: Key not present in map.
Cách 2: Dùng hàm element_at (Thực hành chuẩn - Best Practice) Giống như xử lý mảng, element_at là vị cứu tinh của Data Engineer. Nếu Khóa không tồn tại, nó sẽ êm ái trả về NULL mà không làm sập hệ thống.
SQL
SELECT
product_name,
element_at(specs, 'cpu') AS cpu_type,
element_at(specs, 'vga') AS vga_card -- Trả về NULL nếu không có key 'vga'
FROM (
SELECT 'Laptop' AS product_name, MAP(ARRAY['cpu', 'ram'], ARRAY['Core i7', '16GB']) AS specs
) AS data;
4. Các hàm tiện ích cốt lõi cho MAP
Đôi khi bạn không muốn lấy một giá trị cụ thể, mà muốn kiểm tra xem Từ điển này chứa những Khóa gì.
-
map_keys(map): Rút toàn bộ các Khóa ra thành mộtARRAY. -
map_values(map): Rút toàn bộ các Giá trị ra thành mộtARRAY. -
cardinality(map): Trả về tổng số cặp Key-Value đang có trong từ điển.
Ứng dụng thực tế: Kiểm tra tính trọn vẹn của dữ liệu Giả sử quy định hệ thống yêu cầu mỗi sản phẩm phải có ít nhất 3 thuộc tính.
SQL
SELECT
product_id,
map_keys(attributes) AS list_of_keys,
cardinality(attributes) AS attribute_count
FROM raw_products
WHERE cardinality(attributes) < 3; -- Lọc ra các sản phẩm thiếu thuộc tính
5. Làm phẳng MAP với UNNEST
Cũng giống như ARRAY, kiểu MAP giấu dữ liệu bên trong một ô. Khi bạn cần đưa số liệu này lên BI Dashboard dạng bảng phẳng (dòng và cột tiêu chuẩn), bạn phải dùng UNNEST.
Điểm khác biệt tuyệt vời là khi bung MAP, hàm UNNEST sẽ tự động sinh ra 2 cột: một cột chứa Key và một cột chứa Value. Bạn chỉ cần đặt tên bí danh cho 2 cột đó.
SQL
WITH product_data AS (
SELECT
'P-001' AS product_id,
MAP(ARRAY['color', 'size'], ARRAY['Red', 'XL']) AS attributes
UNION ALL
SELECT
'P-002',
MAP(ARRAY['color', 'weight'], ARRAY['Blue', '2kg'])
)
SELECT
p.product_id,
t.attr_name, -- Cột 1: Chứa Key
t.attr_value -- Cột 2: Chứa Value
FROM product_data AS p
-- Bung MAP ra thành bảng ảo t với 2 cột (attr_name, attr_value)
LEFT JOIN UNNEST(p.attributes) AS t(attr_name, attr_value) ON true;
Kết quả trả về:
| product_id | attr_name | attr_value |
|---|---|---|
| P-001 | color | Red |
| P-001 | size | XL |
| P-002 | color | Blue |
| P-002 | weight | 2kg |
Dữ liệu bây giờ đã trở thành dạng phẳng hoàn toàn, sẵn sàng cho các lệnh GROUP BY hoặc PIVOT trong các công cụ báo cáo.
All Rights Reserved