Trino SQL Bài học 30: Giới thiệu và khởi tạo kiểu dữ liệu mảng (ARRAY)
Mục tiêu bài học:
-
Phá vỡ giới hạn của dữ liệu dạng bảng phẳng (Flat Table) bằng việc lưu trữ nhiều giá trị trong cùng một ô (cell).
-
Nắm vững cú pháp khởi tạo mảng và ép kiểu mảng nghiêm ngặt của Trino.
-
Truy xuất phần tử an toàn và hiểu cạm bẫy "Index bắt đầu từ 1" cực kỳ nguy hiểm đối với lập trình viên.
-
Sử dụng các hàm đo lường và kiểm tra sự tồn tại của phần tử trong mảng.
1. Bản chất của ARRAY trong Trino
Trong cơ sở dữ liệu quan hệ truyền thống, mỗi ô (cell) chỉ được chứa một giá trị duy nhất (đơn nguyên). Nhưng trong Big Data và Data Lake, một dòng log có thể chứa cả một tập hợp dữ liệu (ví dụ: danh sách các mã lỗi, danh sách tag sản phẩm, lịch sử các IP đã truy cập).
Kiểu dữ liệu ARRAY (Mảng) cho phép bạn lưu trữ một danh sách các giá trị vào trong cùng một ô.
Luật ép kiểu tĩnh (Strongly Typed):
Không giống như mảng trong JavaScript hay Python (có thể trộn lẫn số và chữ), mảng trong Trino yêu cầu tất cả các phần tử phải cùng một kiểu dữ liệu.
Bạn sẽ có ARRAY(INTEGER), ARRAY(VARCHAR), ARRAY(DOUBLE), nhưng không bao giờ có mảng trộn lẫn kiểu.
2. Khởi tạo mảng (Initialization)
Có hai cách phổ biến nhất để sinh ra một mảng dữ liệu trong quá trình truy vấn.
Cách 1: Khởi tạo thủ công bằng từ khóa ARRAY
Dùng ngoặc vuông [] để bao bọc các phần tử.
SQL
SELECT
'A101' AS order_id,
ARRAY['Sách', 'Bút', 'Thước'] AS product_tags, -- Mảng chuỗi (ARRAY(VARCHAR))
ARRAY[10, 20, 30] AS discount_codes -- Mảng số nguyên (ARRAY(INTEGER))
Cách 2: Cắt chuỗi thành mảng bằng SPLIT
Như đã "nhá hàng" ở Bài 11, khi đối mặt với dữ liệu lưu dạng chuỗi phân cách bằng dấu phẩy, hàm SPLIT là công cụ giải quyết dứt điểm.
SQL
-- Dữ liệu gốc: 'book,pen,pencil'
SELECT
SPLIT('book,pen,pencil', ',') AS items;
-- Kết quả trả về một ARRAY(VARCHAR): ['book', 'pen', 'pencil']
3. Truy xuất phần tử và Cạm bẫy Index
Cảnh báo Kỹ sư (Developer Trap): Nếu bạn quen viết PHP, Go, hay C++, bạn sẽ có phản xạ lấy phần tử đầu tiên bằng
array[0]. Ở Trino (và chuẩn ANSI SQL), chỉ số mảng (Index) bắt đầu từ 1.
Cách 1: Dùng ngoặc vuông [] (Nguy hiểm)
Bạn có thể dùng [index] để lấy phần tử. Tuy nhiên, nếu index vượt quá số lượng phần tử của mảng, Trino sẽ lập tức báo lỗi Out of bounds và sập câu truy vấn (Fail-fast).
SQL
SELECT ARRAY['A', 'B', 'C'][1] AS phan_tu_dau_tien; -- Kết quả: 'A'
Cách 2: Dùng hàm element_at (Khuyên dùng)
Hàm element_at(array, index) là vũ khí sắc bén nhất để truy xuất mảng vì 2 lý do:
-
Nếu index vượt quá giới hạn, nó trả về
NULLthay vì báo lỗi sập hệ thống. -
Nó hỗ trợ Index âm (Negative Index) để đếm ngược từ cuối mảng lên! (Ví dụ:
-1là phần tử cuối cùng).
SQL
SELECT
tags,
element_at(tags, 1) AS first_tag,
element_at(tags, -1) AS last_tag, -- Lấy phần tử cuối cùng cực nhanh
element_at(tags, 10) AS out_of_bounds -- Trả về NULL an toàn
FROM (
SELECT SPLIT('sale,new,freeship,flashsale', ',') AS tags
) AS dataset;
4. Các hàm tiện ích cốt lõi cho ARRAY
Thay vì phải bóc tách mảng ra để kiểm tra, Trino cung cấp các hàm cho phép bạn "nhìn thấu" vào bên trong mảng ngay lập tức.
A. Đo chiều dài mảng với CARDINALITY()
Tương tự như LENGTH() của chuỗi, CARDINALITY(array) trả về tổng số lượng phần tử đang có trong mảng.
SQL
-- Tìm những đơn hàng mua từ 3 mặt hàng trở lên
SELECT order_id, product_tags
FROM raw_orders
WHERE CARDINALITY(product_tags) >= 3;
B. Kiểm tra phần tử tồn tại với CONTAINS()
Hàm CONTAINS(array, value) trả về TRUE nếu mảng chứa giá trị bạn cần tìm, và FALSE nếu không có. Rất hữu ích khi lọc dữ liệu theo tag.
SQL
-- Lọc ra những khách hàng có gắn tag 'VIP' trong mảng danh sách tag của họ
SELECT user_id, user_tags
FROM user_profiles
WHERE CONTAINS(user_tags, 'VIP');
C. Nối các phần tử mảng thành chuỗi với ARRAY_JOIN()
Đây là hàm đảo ngược của SPLIT. Nó ép các phần tử trong mảng lại thành một chuỗi văn bản duy nhất.
SQL
SELECT ARRAY_JOIN(ARRAY['Trino', 'SQL', 'Bài 30'], ' - ') AS joined_string;
-- Kết quả: 'Trino - SQL - Bài 30'
All Rights Reserved