Vấn đề
Trong Stored Procedure, khi dùng biến người dùng (@variable) để lưu giá trị trước khi UPDATE, giá trị biến sẽ thay đổi sau khi UPDATE vì nó tham chiếu đến cùng một cột.
Giải pháp: Sử dụng biến cục bộ (DECLARE)
Biến cục bộ (DECLARE myVar INT) lưu giá trị snapshot tại thời điểm SELECT, không bị ảnh hưởng bởi UPDATE sau đó.
Ví dụ minh họa
Tạo bảng
mysql> CREATE TABLE DemoTable (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Value INT
);
mysql> INSERT INTO DemoTable(Value) VALUES(100);
Stored Procedure sai (dùng @variable)
DELIMITER //
CREATE PROCEDURE updateValue_Wrong()
BEGIN
SELECT @myValue := (SELECT Value FROM DemoTable WHERE Id = 1);
SELECT @myValue; -- 100
UPDATE DemoTable SET Value = 200 WHERE Id = 1;
SELECT @myValue := (SELECT Value FROM DemoTable WHERE Id = 1);
SELECT @myValue; -- 200 (đã thay đổi!)
END //
DELIMITER ;
Stored Procedure đúng (dùng DECLARE)
DELIMITER //
CREATE PROCEDURE updateValue_Correct()
BEGIN
DECLARE myValue INT;
SELECT Value INTO myValue FROM DemoTable WHERE Id = 1;
SELECT myValue; -- 100 (snapshot)
UPDATE DemoTable SET Value = 200 WHERE Id = 1;
SELECT myValue; -- Vẫn là 100 (giữ nguyên snapshot)
-- Nếu muốn giá trị mới:
SELECT Value INTO myValue FROM DemoTable WHERE Id = 1;
SELECT myValue; -- 200
END //
DELIMITER ;
So sánh kết quả
Gọi procedure sai
mysql> CALL updateValue_Wrong(); +----------+ | @myValue | +----------+ | 100 | +----------+ +----------+ | @myValue | +----------+ | 100 | +----------+ +----------+ | @myValue | +----------+ | 200 | <-- Đã thay đổi! +----------+ +----------+ | @myValue | +----------+ | 200 | +----------+
Gọi procedure đúng
mysql> CALL updateValue_Correct(); +----------+ | myValue | +----------+ | 100 | +----------+ +----------+ | myValue | +----------+ | 100 | <-- Giữ nguyên snapshot +----------+ +----------+ | myValue | +----------+ | 200 | <-- Chỉ sau khi SELECT INTO lại +----------+
Tóm tắt
| Loại biến | Cú pháp | Phạm vi | Hành vi khi UPDATE |
|---|---|---|---|
| User variable | @var | Session | Thay đổi theo dữ liệu real-time |
| Local variable | DECLARE var TYPE | Procedure/Block | Giữ giá trị snapshot |
Kết luận: Luôn dùng DECLARE + SELECT ... INTO để lưu snapshot giá trị trong Stored Procedure.