Ở mấy level trước mình đã nói sơ về procedure và function trong PostgreSQL rồi. Giờ là lúc đào sâu hơn chút nữa.
Function và procedure có thể chạy độc lập, nhưng thường thì sự kết hợp của tụi nó mới quyết định thành công của cả hệ thống. Điểm tiện nhất là function có thể gọi function khác, truyền dữ liệu và thậm chí lấy luôn kết quả trả về.
Function vs Procedure: khác nhau chỗ nào?
Nhớ lại xem function khác procedure ở PostgreSQL như nào nhé:
Function (
FUNCTION):- Trả về giá trị.
- Có thể dùng trong
SELECT. - Thường dùng để tính toán hoặc chuyển đổi dữ liệu.
Procedure (
PROCEDURE):- Không trả về giá trị trực tiếp.
- Dùng để thực hiện thao tác như insert, update hoặc xóa dữ liệu.
- Gọi bằng lệnh
CALL.
Truyền dữ liệu giữa các function
Vào phần thực hành luôn, bắt đầu với ví dụ cơ bản về truyền dữ liệu giữa function và procedure. Thực chất, truyền dữ liệu giữa các function là qua tham số và giá trị trả về.
Đây là ví dụ gọi function trong function khác:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- Lấy tên sinh viên theo ID
SELECT name INTO student_name FROM students WHERE id = student_id;
-- Trả về tên
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
Function này có thể được gọi từ function khác:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- Lấy tên sinh viên bằng function khác
message := 'Chào mừng, ' || get_student_name(student_id) || '!';
-- Trả về lời chào
RETURN message;
END;
$$ LANGUAGE plpgsql;
- Function
get_student_nametrả về tên sinh viên theo id (student_id). - Ở function khác —
welcome_student— tên này được dùng để tạo message chào mừng.
Lưu ý: Lấy dữ liệu bằng SELECT INTO sẽ lưu kết quả truy vấn vào biến PL/pgSQL.
Ví dụ gọi procedure từ function
Giờ thử xem gọi procedure từ function như nào nhé. Giả sử có procedure ghi lại thời gian sinh viên đăng nhập hệ thống:
CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO log_entries(student_id, entry_time)
VALUES (student_id, NOW());
END;
$$;
Giờ gọi procedure này từ function, để vừa log vừa trả về message:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- Gọi procedure để log
CALL log_student_entry(student_id);
-- Trả về message
RETURN 'Đăng nhập sinh viên đã được ghi lại thành công.';
END;
$$ LANGUAGE plpgsql;
Ví dụ thực tế về tương tác
Ví dụ 1: tính tổng đơn hàng và log lại
Giả sử bạn làm hệ thống đặt hàng online. Để tính tổng đơn hàng, bạn có function:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- Tính tổng tất cả các mục trong đơn
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Để lưu tổng đơn hàng, dùng procedure:
CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO order_totals(order_id, total)
VALUES (order_id, total);
END;
$$;
Giờ kết hợp lại:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- Gọi function tính tổng
total := calculate_order_total(order_id);
-- Log tổng qua procedure
CALL log_order_total(order_id, total);
RETURN 'Đơn hàng đã xử lý thành công.';
END;
$$ LANGUAGE plpgsql;
Ví dụ 2: lấy rating cao nhất của sinh viên và cập nhật profile
Function lấy rating cao nhất:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- Tìm rating cao nhất của sinh viên
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
Procedure cập nhật profile sinh viên:
CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE students
SET highest_rating = max_rating
WHERE id = student_id;
END;
$$;
Function gọi hai thao tác trên:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- Lấy rating cao nhất
max_rating := get_highest_rating(student_id);
-- Cập nhật profile sinh viên
CALL update_student_profile(student_id, max_rating);
RETURN 'Profile đã được cập nhật thành công.';
END;
$$ LANGUAGE plpgsql;
Lỗi thường gặp khi tương tác
Một lỗi phổ biến là không khớp kiểu dữ liệu giữa function và procedure. Ví dụ, procedure cần tham số kiểu NUMERIC mà bạn lại truyền INTEGER, PostgreSQL sẽ báo lỗi không khớp kiểu. Luôn kiểm tra kiểu dữ liệu cho khớp nhé.
Lỗi nữa là gọi vòng lặp function, kiểu function A gọi function B, rồi B lại gọi A. Như vậy sẽ bị lặp vô tận và sập hệ thống luôn.
Ý nghĩa thực tế
Tại sao phải tương tác như vậy? Ngoài đời, function và procedure giống như "block xây dựng" của hệ thống phức tạp. Chia nhỏ code ra giúp dễ debug, tái sử dụng và test hơn. Ví dụ:
- Phỏng vấn có thể yêu cầu bạn viết function gọi procedure để xử lý thao tác phức tạp. Thể hiện được kỹ năng này là điểm cộng lớn.
- Khi làm app thực tế như shop online, hệ thống log hay CRM, biết tổ chức code bằng function và procedure sẽ giúp code gọn và dễ quản lý hơn nhiều.
Muốn tìm hiểu sâu hơn về tương tác giữa function và procedure thì đọc thêm tài liệu chính thức về PL/pgSQL nhé.
GO TO FULL VERSION