我尝试使用下面的存储过程在MySQL的动态表中插入数据,但在使用以下命令调用它时遇到错误:
CALL insert_data ('table_x', 'NULL', 'A', 'B', 'C', 'D', 'E ')
错误
Column unknown' 0 'in the list of fields.
操作步骤
DELIMITER $$
CREATE PROCEDURE insertar_datos (name VARCHAR(25), N INT, AP
VARCHAR(15),
AM VARCHAR(15), Nom VARCHAR(30), DNI VARCHAR(8), Direc VARCHAR(30))
BEGIN
SET @tableName = Name;
SET @NName= N;
SET @APName = AP;
SET @AMName = AM;
SET @NomName = Nom;
SET @DNIName = DNI;
SET @DirecName = Direc;
SET @q = CONCAT('
INSERT INTO `' , @tableName, '` VALUES(
`',@NName,'`,
`',@APName,'`,
`',@AMName,'`,
`',@NomName,'`,
`',@DNIName,'`,
`',@DirecName,'`
)
');
PREPARE stmt FROM @q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER;
发布于 2019-03-04 03:34:57
如果您发现自己有多个具有相同列的表,那么您的模式可能设计得很糟糕。如果有一个设计良好的模式,那么您所要求的就不是必需的。我鼓励您提出另一个问题,即如何设计您的模式以完全避免您的问题。
将未过滤的字符串连接在一起容易受到SQL Injection attack的攻击。相反,只要有可能,就使用绑定参数并将值传入。这确保了它们被正确地转义和引用,并且不会被意外或恶意地曲解。
SET @q = 'INSERT INTO `' , @tableName, '` VALUES(?,?,?,?,?,?)';
PREPARE stmt FROM @q;
EXECUTE stmt USING @NName, @APName, @AMName, @NomName, @DNIName, @DirectName
这也解决了你的其他问题,并不是所有东西都是字符串。
CALL insert_data ('table_x', 'NULL', 'A', 'B', 'C', 'D', 'E ')
^^^^
绑定参数将保留非字符串的类型,如整数和空值。我还会注意到,N
是一个整数,但您将其用作名称,这通常意味着字符串。所以这可能是个问题。
现在来看看那个讨厌的表名。我们不能将其作为绑定参数传递。您希望确保它不会跳出引号,所以使用must be escaped. You also don't want anyone to be able to jump to another database, so
.must also be escaped. Or better yet, [raise an error](http://www.mysqltutorial.org/mysql-signal-resignal/). We can't use [
quote()`](https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_quote),因为这是针对具有不同引号规则的列值。我们得写我们自己的。
DELIMITER $$
drop procedure if exists check_table_name;
CREATE PROCEDURE check_table_name (
table_name varchar(255)
)
begin
if( locate("`", table_name) ) then
signal sqlstate '45000'
set message_text = 'illegal ` in table name';
elseif( locate(".", table_name) ) then
signal sqlstate '45000'
set message_text = 'illegal . in table name';
end if;
end $$
DELIMITER ;
现在,我们在使用它之前对表名调用它。
call check_table_name(@tableName);
SET @q = concat('INSERT INTO `' , @tableName, '` VALUES(?,?,?,?,?,?)');
PREPARE stmt FROM @q;
EXECUTE stmt USING @NName, @APName, @AMName, @NomName, @DNIName, @DirecName;
DEALLOCATE PREPARE stmt;
如果它是淘气的,我们会得到一个错误。
mysql> call insertar_datos("foo`haha, I broke your quoting`bar", 23, 'A', 'B', 'C', 'D', 'E ');
ERROR 1644 (45000): illegal ` in table name
但是,,我强烈反对这种方法。任何时候,当您将字符串连接在一起以生成SQL语句时,就有可能出现bug和安全缺陷。我不相信所有的问题都是check_table_name
造成的。
如果您必须采用这种方法,请考虑改用一组固定的允许名称。
if name in ("foo", "bar", "baz", "table_x") then
set @tableName = name;
else
signal sqlstate '45000'
set message_text = 'unknown name';
end if;
我鼓励您重新考虑重新设计您的模式。
https://stackoverflow.com/questions/54972244
复制相似问题