Gán kết quả truy vấn động vào biến với Prepared Statement trong MySQL
Sử dụng Stored Procedure kết hợp Prepared Statement (PREPARE, EXECUTE, DEALLOCATE PREPARE) để xây dựng truy vấn động và gán kết quả vào biến session (@variable).
Ví dụ minh họa
Tạo bảng DemoTable:
mysql> CREATE TABLE DemoTable (Id INT, Name VARCHAR(100)); Query OK, 0 rows affected (1.51 sec)
Chèn dữ liệu:
mysql> INSERT INTO DemoTable VALUES(10, 'John'); Query OK, 1 row affected (0.17 sec) mysql> INSERT INTO DemoTable VALUES(11, 'Chris'); Query OK, 1 row affected (0.41 sec)
Xem dữ liệu:
mysql> SELECT * FROM DemoTable;
+------+-------+ | Id | Name | +------+-------+ | 10 | John | | 11 | Chris | +------+-------+ 2 rows in set (0.00 sec)
Tạo Stored Procedure nhận tên bảng, tên cột, ID - trả về giá trị vào biến @value:
mysql> DELIMITER //
mysql> CREATE PROCEDURE Prepared_Statement_Demo(
IN nameOfTable VARCHAR(20),
IN nameOfColumn VARCHAR(20),
IN idColumnName INT
)
BEGIN
SET @holdResult = CONCAT('SELECT ', nameOfColumn, ' INTO @value FROM ', nameOfTable, ' WHERE Id = ', idColumnName);
PREPARE st FROM @holdResult;
EXECUTE st;
DEALLOCATE PREPARE st;
END //
Query OK, 0 rows affected (0.20 sec)
mysql> DELIMITER ;
Gọi Procedure:
mysql> CALL Prepared_Statement_Demo('DemoTable', 'Name', 10);
Query OK, 0 rows affected, 2 warnings (0.00 sec)
Kiểm tra biến @value:
mysql> SELECT @value;
+--------+ | @value | +--------+ | John | +--------+ 1 row in set (0.00 sec)
Giải thích quy trình
CONCAT(...): Xây dựng chuỗi SQL độngPREPARE st FROM @holdResult: Biên dịch statement từ chuỗiEXECUTE st: Thực thi statement đã chuẩn bịDEALLOCATE PREPARE st: Giải phóng prepared statementSELECT ... INTO @value: Gán kết quả trực tiếp vào biến session
⚠️ Cảnh báo bảo mật (SQL Injection)
Ví dụ này dễ bị SQL Injection vì nối chuỗi trực tiếp tham số đầu vào. Trong production:
- Dùng Parameterized Prepared Statement với placeholder
? - Validate/whitelist tên bảng, tên cột (không cho user nhập tự do)
- Hoặc dùng ORM/Query Builder an toàn
Phiên bản an toàn với Parameterized Query
CREATE PROCEDURE Safe_Demo(IN tableName VARCHAR(64), IN colName VARCHAR(64), IN idVal INT)
BEGIN
-- Whitelist validation
IF tableName NOT IN ('DemoTable','OtherTable') OR colName NOT IN ('Name','Email') THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid table/column';
END IF;
SET @sql = CONCAT('SELECT ', colName, ' INTO @result FROM ', tableName, ' WHERE Id = ?');
PREPARE stmt FROM @sql;
SET @id = idVal;
EXECUTE stmt USING @id;
DEALLOCATE PREPARE stmt;
END