PERFORM trong thế giới PostgreSQL giống như một anh hùng trầm lặng, đến, làm việc của mình rồi biến mất, không để lại dấu vết gì kiểu dữ liệu trả về. Lệnh này dùng khi bạn muốn chạy một truy vấn SQL trong hàm PL/pgSQL, nhưng không cần xử lý hay lưu kết quả. Nhiệm vụ chính của PERFORM là gọi truy vấn để có điều gì đó xảy ra, ví dụ như sửa đổi dữ liệu hoặc gọi hàm khác, chứ không phải lấy kết quả về.
PERFORM dùng trong trường hợp bạn không quan tâm đến kết quả truy vấn. Khác với SELECT thông thường, vốn đòi hỏi bạn phải làm gì đó với kết quả, PERFORM chỉ chạy truy vấn rồi lặng lẽ đi tiếp. Cực kỳ tiện nếu bạn gọi hàm chỉ để nó thực hiện hành động nào đó, chứ không phải vì giá trị trả về. Ví dụ, ghi log. Cách này làm code gọn hơn, dễ hiểu hơn: ít rườm rà, nhiều ý nghĩa.
Ví dụ sử dụng PERFORM
Gọi hàm
Cùng xem ví dụ thực tế nhé. Giả sử bạn có hàm log_action để ghi thông tin hành động của user vào log. Hàm này không trả về gì, và bạn chỉ muốn nó chạy thôi. Đây là cách dùng PERFORM:
CREATE OR REPLACE FUNCTION log_action(user_id INT, action TEXT) RETURNS VOID AS $$
BEGIN
INSERT INTO logs (user_id, action, log_time)
VALUES (user_id, action, NOW());
END;
$$ LANGUAGE plpgsql;
-- Tiếp theo dùng PERFORM để gọi hàm này:
PERFORM log_action(5, 'Người dùng đã đăng nhập');
Ở đây có gì? Lệnh PERFORM gọi hàm log_action, hàm này ghi một dòng vào bảng logs. Bạn nhớ nhé: kết quả trả về của hàm bị bỏ qua. Mình dùng nó chỉ vì hiệu ứng phụ, không phải vì giá trị trả về.
Cập nhật dữ liệu
Đôi khi PERFORM hữu ích khi bạn cần chạy truy vấn để thay đổi dữ liệu mà không quan tâm đến kết quả. Ví dụ, cập nhật trạng thái đơn hàng trong bảng orders.
CREATE OR REPLACE FUNCTION update_order_status(order_id INT, new_status TEXT) RETURNS VOID AS $$
BEGIN
UPDATE orders
SET status = new_status
WHERE id = order_id;
END;
$$ LANGUAGE plpgsql;
-- Dùng PERFORM để gọi hàm này:
PERFORM update_order_status(101, 'Đã gửi hàng');
Ở đây update_order_status cập nhật trạng thái đơn hàng có id 101. Mình không quan tâm đến kết quả truy vấn SQL trong hàm, nên PERFORM là lựa chọn hoàn hảo.
Chạy thao tác phụ trợ
Đôi khi hàm chứa các thao tác nhỏ kiểu "phụ trợ", giúp hoàn thành một quy trình phức tạp. Ví dụ, bạn muốn xóa cache sau khi cập nhật bảng:
CREATE OR REPLACE FUNCTION clear_cache() RETURNS VOID AS $$
BEGIN
DELETE FROM cache_table;
END;
$$ LANGUAGE plpgsql;
-- Gọi nó trong hàm khác:
CREATE OR REPLACE FUNCTION update_product(product_id INT, new_price NUMERIC) RETURNS VOID AS $$
BEGIN
UPDATE products
SET price = new_price
WHERE id = product_id;
-- Xóa cache sau khi thay đổi dữ liệu:
PERFORM clear_cache();
END;
$$ LANGUAGE plpgsql;
Đây là chỗ hay ho: bạn có thể thực hiện liên tiếp các hành động, dùng PERFORM để gọi hàm mà bạn hoàn toàn không cần kết quả trả về.
Bài tập thực tế
Cùng xem vài ví dụ về cách PERFORM giúp dev đỡ cực hơn nhé.
Ví dụ 1: ghi log các bước thực hiện thủ tục
Giả sử bạn có một thủ tục xử lý thanh toán phức tạp, cần theo dõi từng giai đoạn và ghi log lại. Bạn có thể định nghĩa hàm log_stage để ghi thông tin, rồi dùng PERFORM:
CREATE OR REPLACE FUNCTION log_stage(stage_name TEXT) RETURNS VOID AS $$
BEGIN
INSERT INTO process_logs(stage, log_time)
VALUES (stage_name, NOW());
END;
$$ LANGUAGE plpgsql;
-- Đây là ví dụ thủ tục:
CREATE OR REPLACE FUNCTION process_payment(payment_id INT) RETURNS VOID AS $$
BEGIN
-- Ghi log bắt đầu
PERFORM log_stage('Bắt đầu xử lý thanh toán');
-- Thực hiện bước đầu tiên
UPDATE payments
SET status = 'Đang xử lý'
WHERE id = payment_id;
PERFORM log_stage('Đã cập nhật trạng thái thanh toán');
-- Thực hiện bước cuối
UPDATE payments
SET status = 'Hoàn thành'
WHERE id = payment_id;
PERFORM log_stage('Đã hoàn thành thanh toán');
END;
$$ LANGUAGE plpgsql;
Ở đây log_stage được gọi qua PERFORM để ghi trạng thái thực hiện ở mỗi bước của thủ tục. Nhờ vậy code dễ debug hơn nhiều.
Ví dụ 2: kích hoạt thông báo
Giả sử bạn có hệ thống thông báo, cần gửi thông báo sau mỗi hành động quan trọng. PERFORM có thể dùng để gọi hàm phụ trách việc này:
CREATE OR REPLACE FUNCTION send_notification(user_id INT, message TEXT) RETURNS VOID AS $$
BEGIN
INSERT INTO notifications (user_id, message, created_at)
VALUES (user_id, message, NOW());
END;
$$ LANGUAGE plpgsql;
-- Dùng trong thủ tục:
CREATE OR REPLACE FUNCTION complete_task(task_id INT) RETURNS VOID AS $$
DECLARE
user_id INT;
BEGIN
-- Lấy tác giả task
SELECT assigned_to INTO user_id
FROM tasks
WHERE id = task_id;
-- Hoàn thành task
UPDATE tasks
SET status = 'Hoàn thành'
WHERE id = task_id;
-- Gửi thông báo
PERFORM send_notification(user_id, 'Nhiệm vụ của bạn đã hoàn thành');
END;
$$ LANGUAGE plpgsql;
Ở đây PERFORM giúp bạn chỉ tập trung vào hiệu ứng phụ - gửi thông báo, bỏ qua kết quả thực thi của hàm.
Tips hữu ích và lỗi thường gặp
Khi dùng PERFORM, nhớ một số điểm quan trọng. Ví dụ, PERFORM không kiểm tra xem truy vấn có trả về dữ liệu không. Nghĩa là nếu kết quả của hàm hoặc truy vấn SQL quan trọng cho logic, tốt nhất nên dùng SELECT INTO. Xem ví dụ nhé:
-- Lỗi tiềm ẩn
PERFORM some_function_that_must_return_value();
-- Sửa đúng
SELECT some_function_that_must_return_value() INTO some_variable;
Một lỗi phổ biến nữa là dùng PERFORM ở chỗ bạn thực sự cần lấy kết quả truy vấn, ví dụ kiểm tra dữ liệu. Trong trường hợp đó, tất nhiên nên lấy kết quả và kiểm tra nó.
Trong dự án thực tế, lệnh PERFORM giúp hàm và thủ tục đơn giản, dễ đọc và dễ debug hơn. Kết hợp với ghi log (RAISE NOTICE) và các hàm chẩn đoán tích hợp của PostgreSQL như current_query(), nó trở thành công cụ quan trọng để xây dựng hệ thống ổn định, dễ kiểm soát và dễ hiểu.
GO TO FULL VERSION