Trino SQL Bài học 32: Xử lý mảng nâng cao với Lambda Functions (FILTER, REDUCE, TRANSFORM)
Mục tiêu bài học:
-
Hiểu và viết được cú pháp Lambda (Hàm ẩn danh) trực tiếp trong SQL.
-
Áp dụng tư duy lập trình hàm (Functional Programming) để thao tác với mảng mà không cần dùng đến
UNNESThayGROUP BY. -
Dùng
TRANSFORM(Map),FILTERvàREDUCEđể biến đổi, lọc và tổng hợp dữ liệu mảng ngay tại dòng hiện tại, tối ưu triệt để I/O. -
Tránh nhầm lẫn tên gọi: Trong Trino, thao tác ánh xạ mảng gọi là
TRANSFORMthay vìMAP(vìMAPđược dành riêng cho kiểu dữ liệu Từ điển/Dictionary).
1. Kỷ nguyên của Lambda Functions trong SQL
Trong môi trường Backend (như PHP, Go, C#), việc sử dụng các hàm Lambda (ẩn danh) dạng x => x + 1 để duyệt mảng là thao tác vô cùng quen thuộc. Trino mang trọn vẹn sức mạnh này vào SQL, cho phép bạn lặp qua từng phần tử của mảng và xử lý chúng ngay trên bộ nhớ (In-memory) của từng Worker, bỏ qua hoàn toàn bước "làm phẳng" tốn kém của UNNEST.
Cú pháp Lambda chuẩn của Trino: Sử dụng toán tử mũi tên ->.
-
Một tham số:
x -> x * 2(Nhân đôi giá trị x). -
Hai tham số:
(x, y) -> x + y(Tính tổng x và y).
2. TRANSFORM: Ánh xạ và Biến đổi (Tương đương MAP)
Hàm TRANSFORM duyệt qua mọi phần tử trong mảng, áp dụng hàm Lambda lên từng phần tử và trả về một mảng mới có cùng kích thước.
Cú pháp: TRANSFORM(array, lambda_function)
Ví dụ thực tế: Giả sử hệ thống (ví dụ API nội bộ) ghi nhận mảng giá tiền của các mặt hàng trong đơn, bạn muốn áp dụng mã giảm giá 10% cho toàn bộ phần tử trong mảng đó.
SQL
SELECT
order_id,
prices AS original_prices,
-- Duyệt qua từng phần tử (p), nhân với 0.9 và trả về mảng mới
TRANSFORM(prices, p -> p * 0.9) AS discounted_prices
FROM (
SELECT 'ORD-01' AS order_id, ARRAY[100000, 50000, 20000] AS prices
) AS data;
Kết quả: Mảng [100000, 50000, 20000] sẽ biến thành [90000.0, 45000.0, 18000.0] ngay trên cùng một dòng.
3. FILTER: Lọc dữ liệu tàn dư bên trong mảng
Nếu WHERE dùng để lọc các dòng trong bảng, thì FILTER dùng để vứt bỏ các phần tử không thỏa mãn điều kiện ở bên trong một mảng.
Cú pháp: FILTER(array, lambda_function)
Ứng dụng "Dọn rác" (Data Cleansing): Log hệ thống đôi khi sinh ra các mảng chứa giá trị NULL hoặc rỗng (do lỗi tracking frontend). Bạn có thể dùng FILTER để làm sạch mảng trước khi lưu trữ.
SQL
SELECT
user_id,
-- Chỉ giữ lại các phần tử (x) khác NULL và có độ dài lớn hơn 0
FILTER(tags, x -> x IS NOT NULL AND LENGTH(TRIM(x)) > 0) AS clean_tags
FROM (
SELECT 101 AS user_id, ARRAY['vip', NULL, ' ', 'active'] AS tags
) AS data;
Kết quả: Mảng gốc bị loại bỏ NULL và khoảng trắng, chỉ còn lại mảng sạch ['vip', 'active'].
4. REDUCE: Nén mảng thành một giá trị (Gom cụm nội dòng)
Hàm REDUCE là cấp độ cao nhất của xử lý mảng. Nó dùng để "gấp" (fold) toàn bộ mảng lại thành một giá trị vô hướng (Scalar Value) duy nhất, ví dụ: Tính tổng các phần tử trong mảng.
Cú pháp: REDUCE(array, trạng_thái_khởi_tạo, hàm_cộng_dồn, hàm_trả_kết_quả)
-
trạng_thái_khởi_tạo: Giá trị bắt đầu (ví dụ:0nếu tính tổng,1nếu tính tích). -
hàm_cộng_dồn: Hàm Lambda 2 tham số(s, x), trong đóslà trạng thái tích lũy hiện tại,xlà phần tử đang duyệt. -
hàm_trả_kết_quả: Hàm Lambda 1 tham số để định dạng kết quả cuối cùng (thường viết đơn giản làs -> s).
Ví dụ: Tính tổng doanh thu từ mảng các giá trị hóa đơn.
SQL
SELECT
session_id,
cart_items_prices,
-- Tính tổng mảng: Bắt đầu từ 0, mỗi bước cộng thêm giá trị x vào tổng s
REDUCE(cart_items_prices, 0, (s, x) -> s + x, s -> s) AS total_cart_value
FROM (
SELECT 'S-123' AS session_id, ARRAY[150000, 30000, 20000] AS cart_items_prices
) AS data;
Kết quả: 3 phần tử trong mảng được nén lại thành một con số duy nhất là 200000. Bạn vừa thực hiện một phép tính tổng hợp (SUM) mà không hề cần đến UNNEST hay GROUP BY.
5. ANY_MATCH, ALL_MATCH và NONE_MATCH
Đây là 3 hàm Boolean chuyên dụng để kiểm tra điều kiện logic trên mảng cực kỳ tốc độ. Nó trả về TRUE hoặc FALSE.
-
ANY_MATCH(array, lambda): Đúng nếu CÓ ÍT NHẤT MỘT phần tử thỏa mãn điều kiện. -
ALL_MATCH(array, lambda): Đúng nếu TẤT CẢ phần tử đều thỏa mãn điều kiện. -
NONE_MATCH(array, lambda): Đúng nếu KHÔNG CÓ phần tử nào thỏa mãn.
SQL
SELECT
server_id,
error_codes,
-- Kích hoạt cảnh báo nếu mảng log có chứa mã lỗi nghiêm trọng (Bắt đầu bằng chữ 'FATAL')
ANY_MATCH(error_codes, e -> e LIKE 'FATAL%') AS is_critical_failure
FROM (
SELECT 'srv-01' AS server_id, ARRAY['WARN_1', 'INFO', 'FATAL_MEMORY'] AS error_codes
UNION ALL
SELECT 'srv-02', ARRAY['INFO', 'INFO']
) AS data;
All Rights Reserved