0

Trino SQL Bài học 14: Chuyển đổi và ép kiểu dữ liệu an toàn (CAST, TRY_CAST)

Mục tiêu bài học:

  • Hiểu rõ triết lý "Fail-fast" (Lỗi là dừng ngay) của Trino khi đối mặt với dữ liệu sai định dạng.

  • Sử dụng CAST để chuyển đổi qua lại giữa các kiểu dữ liệu chuẩn mực.

  • Nắm vững "vũ khí bí mật" TRY_CAST để bảo vệ hệ thống không bị sập khi gặp dữ liệu rác (Dirty Data).

  • Kết hợp TRY_CAST và COALESCE để tạo ra một cơ chế làm sạch dữ liệu hoàn hảo.

1. Triết lý "Kiểu tĩnh" và sự cần thiết của việc ép kiểu

Như đã đề cập ở Bài 7, Trino là một hệ thống Strongly Typed (Kiểu tĩnh khắt khe). Trino không đoán mò ý định của bạn.

Nếu ở MySQL, bạn có thể thực hiện phép cộng SELECT 5 + '10', hệ thống sẽ tự động hiểu '10' là số và trả về 15 (Implicit Cast). Nhưng ở Trino, câu lệnh trên sẽ lập tức báo lỗi: Cannot apply operator: integer + varchar.

Để thực hiện các phép toán hoặc ghép chuỗi giữa các họ dữ liệu khác nhau, bạn bắt buộc phải chuyển đổi chúng về cùng một kiểu dữ liệu một cách tường minh (Explicit).

2. Ép kiểu tiêu chuẩn với CAST

Mệnh đề CAST là cú pháp chuẩn của ANSI SQL, được dùng khi bạn chắc chắn 100% dữ liệu nguồn hoàn toàn sạch và đúng định dạng.

Cú pháp: CAST(giá_trị AS kiểu_dữ_liệu_đích)

Ứng dụng 1: Đổi Số thành Chuỗi (Để ghép nối text)

SQL

-- Lỗi nếu viết: 'Khách hàng ID: ' || custkey (vì custkey là số)
-- Viết ĐÚNG:
SELECT 
    'Khách hàng ID: ' || CAST(custkey AS VARCHAR) AS customer_label
FROM tpch.tiny.customer;

Ứng dụng 2: Đổi Chuỗi thành Số (Để tính toán)

SQL

-- Giả sử bạn có cột 'discount_rate' đang bị lưu nhầm dưới dạng chuỗi '0.05'
SELECT 
    orderkey, 
    totalprice,
    totalprice * CAST('0.05' AS DOUBLE) AS discount_amount
FROM tpch.tiny.orders;

Ứng dụng 3: Đổi Chuỗi thành Thời gian Nếu chuỗi tuân thủ đúng định dạng ISO (YYYY-MM-DD), bạn có thể dùng CAST thay vì date_parse cho gọn.

SQL

SELECT CAST('2026-10-11' AS DATE) AS date_val;

3. Thảm họa Data Lake và cứu tinh TRY_CAST

Vấn đề: Hệ sinh thái Trino thường được dùng để query trực tiếp vào Data Lake (nơi chứa các file log CSV, JSON do người dùng tự nhập). Dữ liệu này thường rất "bẩn". Giả sử bạn có 1 bảng user_logs với 1 tỷ dòng. Cột age (tuổi) đang lưu dưới dạng chuỗi (VARCHAR). Trong 1 tỷ dòng đó, có đúng 1 dòng người dùng nhập nhầm chữ "hai mươi" thay vì số "20".

Nếu bạn dùng CAST(age AS INTEGER), khi Worker quét đến dòng chữ "hai mươi", nó không thể ép thành số. Trino sẽ lập tức báo lỗi (Fail-fast) và hủy bỏ toàn bộ truy vấn của bạn, dù nó đã chạy mất 30 phút.

Giải pháp: TRY_CAST Để hệ thống dung lỗi (Fault-tolerant), Trino cung cấp hàm TRY_CAST.

  • Nếu dữ liệu hợp lệ: Trả về giá trị đã ép kiểu.

  • Nếu dữ liệu bị lỗi (không thể ép kiểu): Trả về giá trị NULL (và truy vấn vẫn tiếp tục chạy mượt mà cho đến hết tỷ dòng).

SQL

-- Dùng TRY_CAST để ép kiểu an toàn
SELECT 
    TRY_CAST('20' AS INTEGER) AS hop_le,          -- Kết quả: 20
    TRY_CAST('hai mươi' AS INTEGER) AS loi_data,  -- Kết quả: NULL (Không bị sập hệ thống!)
    TRY_CAST('2026/10/11' AS DATE) AS sai_format; -- Kết quả: NULL

4. Combo hoàn hảo: TRY_CAST + COALESCE

Bây giờ, khi TRY_CAST đã biến những dữ liệu rác thành NULL, bạn sẽ dùng kiến thức ở Bài 10 (COALESCE) để gán cho nó một giá trị mặc định, khép lại một quy trình làm sạch dữ liệu (Data Cleansing) hoàn chỉnh ngay trong câu SQL.

SQL

-- Nếu cột string_age chứa chữ linh tinh không ép thành số được, 
-- TRY_CAST sẽ biến nó thành NULL.
-- COALESCE sẽ đón lõng cái NULL đó và biến nó thành số 0 (tuổi mặc định).
SELECT 
    user_id,
    COALESCE(TRY_CAST(string_age AS INTEGER), 0) AS safe_age
FROM raw_user_logs;

5. Khối bảo vệ mở rộng: Hàm TRY()

Ngoài lỗi ép kiểu, còn rất nhiều lỗi toán học có thể làm sập truy vấn (như chia cho 0, hoặc mảng bị vượt quá chỉ mục - out of bounds). Trino cung cấp hàm TRY(biểu_thức) để bọc bất kỳ biểu thức tính toán nào có rủi ro cao.

Giống như TRY_CAST, nếu biểu thức bên trong TRY() sinh ra lỗi, nó sẽ nuốt lỗi đó và trả về NULL.

SQL

-- Nếu quantity = 0, phép chia sẽ sinh lỗi. 
-- TRY() sẽ bắt lỗi và trả về NULL cho dòng đó.
SELECT 
    orderkey,
    TRY(totalprice / quantity) AS safe_unit_price
FROM tpch.tiny.lineitem;

(Ghi chú: Ở Bài 10 bạn đã dùng NULLIF để xử lý chia cho 0. TRY là một công cụ mạnh mẽ hơn, bắt được mọi loại lỗi, nhưng bù lại hiệu năng xử lý của nó sẽ chậm hơn một chút so với NULLIF hoặc TRY_CAST thuần túy).


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí