0

Google BigQuery - Bài học 19: Procedural SQL Scripting – Sử dụng biến, vòng lặp và điều kiện trong BigQuery

Chào anh em! Chúng ta đã chính thức khép lại Phần 2 với vô số kỹ thuật tối ưu hóa câu lệnh truy vấn GoogleSQL siêu đỉnh. Hôm nay, chúng ta bước sang một chương hoàn toàn mới: Phần 3: Lập trình hóa SQL, Hàm tự định nghĩa & GIS.

Nếu như trước đây, mỗi lần bạn muốn chạy một chuỗi các câu lệnh SQL liên quan mật thiết với nhau (ví dụ: Bước 1 tạo bảng tạm, bước 2 kiểm tra số lượng dòng, bước 3 quyết định xem nên chèn dữ liệu vào bảng chính hay hủy bỏ), bạn buộc phải nhờ đến các công cụ bên ngoài như Python, Airflow, hoặc viết script bash gọi lệnh bq.

Nhưng giờ đây, với tính năng Procedural SQL Scripting của BigQuery, bạn có thể biến các đoạn mã SQL thuần túy thành một chương trình thực thụ với đầy đủ Biến (DECLARE), Điều kiện (IF/ELSE), Vòng lặp (LOOP/WHILE), và khối xử lý ngoại lệ ngay bên trong giao diện BigQuery Studio!

Hãy cùng xém cách chúng ta "lập trình hóa" tầng dữ liệu ngay bây giờ nhé!

1. Biến số trong BigQuery: Khai báo (DECLARE) và Gán giá trị (SET)

Cũng giống như khi viết code bằng Go, Node.js hay Python, trước khi muốn lưu trữ tạm thời một giá trị nào đó (như tổng doanh thu, một mốc thời gian, hoặc một mã ID), bạn phải khai báo biến.

Trong BigQuery, mọi kịch bản Procedural Scripting thường được bao bọc bên trong một khối BEGIN ... END (hoặc viết trực tiếp thành một chuỗi lệnh chạy liên tiếp).

Ví dụ khai báo và sử dụng biến cơ bản:

SQL

-- 1. Khai báo biến với từ khóa DECLARE và kiểu dữ liệu
DECLARE target_date DATE;
DECLARE total_revenue NUMERIC;
DECLARE store_name STRING;

-- 2. Gán giá trị cho biến bằng từ khóa SET hoặc dùng kết quả từ câu lệnh SELECT ... INTO
SET target_date = '2026-10-18';
SET store_name = 'Chi nhánh Hồ Chí Minh';

-- 3. Đưa biến vào câu lệnh SELECT truy vấn chính
SET total_revenue = (
    SELECT SUM(total_amount) 
    FROM `my-project.core_dwh.fact_orders`
    WHERE DATE(created_at) = target_date
);

-- 4. In kết quả ra màn hình (Log ra cửa sổ kết quả)
SELECT target_date AS NgayBaoCao, store_name AS KhuVuc, total_revenue AS DoanhThuTong;

2. Rẽ nhánh logic với IF ... THEN ... ELSE

Khi xây dựng các kịch bản ETL tự động, bạn thường xuyên phải đối mặt với bài toán điều kiện: Nếu hôm nay là Chủ Nhật thì chạy báo cáo tổng kết tuần, ngược lại chỉ chạy báo cáo ngày.

Cấu trúc IF trong GoogleSQL vô cùng trực quan:

SQL

DECLARE record_count INT64;

-- Đếm số lượng đơn hàng chưa xử lý
SET record_count = (
    SELECT COUNT(*) 
    FROM `my-project.staging.queue_orders` 
    WHERE status = 'PENDING'
);

-- Kiểm tra điều kiện để rẽ nhánh xử lý
IF record_count > 10000 THEN
    -- Nếu lượng đơn hàng tồn quá lớn, thực hiện ghi log cảnh báo
    SELECT CONCAT('CẢNH BÁO: Đang có ', CAST(record_count AS STRING), ' đơn hàng đang bị nghẽn!') AS alert_message;
    
-- Điều kiện phụ (Tùy chọn)
ELSEIF record_count > 0 THEN
    SELECT 'Số lượng đơn hàng ở mức bình thường, tiến hành xử lý tiêu chuẩn.' AS message;
    
-- Trường hợp còn lại
ELSE
    SELECT 'Không có đơn hàng nào cần xử lý.' AS message;
END IF;

3. Vòng lặp (WHILE & LOOP) – Giải cứu các bài toán Batch Processing lớn

Trong thế giới Data Warehouse, việc bạn cố gắng chạy một câu lệnh INSERT hoặc UPDATE cho hàng tỷ dòng dữ liệu trong một câu lệnh duy nhất (Single Big Bang Transaction) thường dẫn đến việc tràn tài nguyên Slot hoặc chạm giới hạn thời gian thực thi tối đa (Timeout sau 6 giờ).

Kỹ thuật chuẩn mực của một Data Engineer là chia nhỏ dữ liệu ra thành từng mẻ (Batch Processing) chạy theo ngày hoặc theo ID bằng vòng lặp.

Ví dụ dùng vòng lặp WHILE để chạy dồn dữ liệu theo từng ngày:

Giả sử bạn cần đồng bộ dữ liệu lịch sử trong 7 ngày gần đây, thay vì chạy một câu lệnh khổng lồ, bạn dùng vòng lặp lùi dần từng ngày:

SQL

-- Khai báo biến ngày bắt đầu chạy
DECLARE current_processing_date DATE;
SET current_processing_date = CURRENT_DATE('Asia/Ho_Chi_Minh') - 7;

-- Vòng lặp WHILE chạy cho đến khi chạm đến ngày hôm nay
WHILE current_processing_date <= CURRENT_DATE('Asia/Ho_Chi_Minh') - 1 DO
    
    -- In ra thông báo đang xử lý ngày nào
    SELECT CONCAT('Đang tổng hợp dữ liệu cho ngày: ', CAST(current_processing_date AS STRING)) AS process_log;

    -- Thực hiện câu lệnh INSERT gom nhóm dữ liệu của đúng ngày hôm đó
    INSERT INTO `my-project.mart_sales.daily_summary` (summary_date, total_orders, total_revenue)
    SELECT 
        current_processing_date,
        COUNT(order_id),
        SUM(total_amount)
    FROM `my-project.core_dwh.fact_orders`
    WHERE DATE(DATETIME(created_at, 'Asia/Ho_Chi_Minh')) = current_processing_date;

    -- Tăng biến đếm lên 1 ngày để sang vòng lặp tiếp theo
    SET current_processing_date = DATE_ADD(current_processing_date, INTERVAL 1 DAY);

END WHILE;

4. Thực chiến nâng cao: Dynamic SQL với EXECUTE IMMEDIATE

Đôi khi, bạn muốn tên bảng hoặc tên dataset thay đổi linh hoạt theo biến số truyền vào (ví dụ: chạy báo cáo cho từng tháng động như orders_2026_01, orders_2026_02...). Trong SQL tĩnh, bạn không thể truyền trực tiếp biến vào vị trí tên bảng.

Đó là lúc cú pháp EXECUTE IMMEDIATE xuất hiện – cho phép bạn ghép chuỗi thành một câu lệnh SQL hoàn chỉnh rồi bắt Dremel thực thi ngay lập tức!

SQL

DECLARE target_table_name STRING;
DECLARE sql_query STRING;

-- Xác định tên bảng động dựa trên tháng hiện tại
SET target_table_name = 'my-project.core_dwh.fact_orders_2026_10';

-- Xây dựng chuỗi câu lệnh SQL động
SET sql_query = FORMAT("""
    SELECT count(*) as total_rows 
    FROM `%s`
    WHERE status = 'COMPLETED'
""", target_table_name);

-- Thực thi chuỗi câu lệnh SQL vừa tạo
EXECUTE IMMEDIATE sql_query;

Tổng kết Bài học 19

Procedural SQL Scripting đã biến Google BigQuery từ một công cụ truy vấn thuần túy thành một nền tảng xử lý logic dữ liệu mạnh mẽ:

  1. Biến (DECLARE / SET): Lưu trữ giá trị tạm thời để tái sử dụng trong suốt kịch bản.

  2. Rẽ nhánh (IF...THEN...ELSE): Linh hoạt kiểm soát luồng thực thi dựa trên các điều kiện thực tế của dữ liệu.

  3. Vòng lặp (WHILE / LOOP): Chia nhỏ các tác vụ quét dữ liệu khổng lồ thành các mẻ nhỏ an toàn, chống tràn tài nguyên Slots.

  4. Dynamic SQL (EXECUTE IMMEDIATE): Linh hoạt xử lý các bảng hoặc dataset thay đổi tên theo thời gian.

Tuy nhiên, điều gì xảy ra nếu bên trong vòng lặp hoặc khối lệnh Procedural đó, một câu lệnh SQL bị lỗi (ví dụ: bảng nguồn bị mất, kiểu dữ liệu không khớp)? Chương trình sẽ sập nguồn ngay lập tức. Làm thế nào để bắt lỗi tự động và ghi log an toàn?

Hẹn gặp lại anh em ở Bài học 20: Stored Procedures & Khối BEGIN...EXCEPTION – Đóng gói quy trình ETL và bắt lỗi tự động!


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í