PHP/MySQL中INSERT INTO报错:子查询返回多行(无显式子查询)
问题:INSERT语句无子查询却返回“Subquery returns more than 1 row”错误
我正在用PHP和MySQL开发网站,执行这段PHP代码时遇到了奇怪的错误:
$sql = sprintf("INSERT INTO request_boms SET request_list_id = %d, project_version_id = %d, amount = %d, user = '%s', timestamp = %d", $requestNo, $bomID, $amount, $_SESSION["admin_user_name"], time()); $this->db_inventory->Execute($sql);
已经确认$this->db_inventory的数据库连接是正常的,但执行后返回错误:Subquery returns more than 1 row,对应的SQL语句是:
INSERT INTO request_boms SET request_list_id = 14, project_version_id = 429, amount = 1, user = 'admin', timestamp = 1607510083
我查了相关案例,发现所有案例的查询里都包含SELECT子查询,但我的INSERT语句根本没用到SELECT,这到底是怎么回事?
补充一下我的Execute()方法和RecordSet类代码:
public function Execute($sql) { $rs = new RecordSet($sql, $this->db_type, $this->conn); return $rs; }
class RecordSet { public $rs; public $db_type; public $conn; public function RecordSet($sql, $db_type, $conn) { $this->db_type = $db_type; $this->conn = $conn; if ($this->db_type == 'sybase') { $rsx =@sybase_query($this->sql_escape($sql)); if (!$rsx) { $this->queryError(sybase_get_last_message(),$sql); } } elseif ($this->db_type == 'mssql') { $rsx =@mssql_query($this->sql_escape($sql)); if (!$rsx) { $this->queryError(mssql_get_last_message(),$sql); } } elseif ($this->db_type == 'odbc') { $rsx =@odbc_exec($this->conn, $this->sql_escape($sql)); if (!$rsx) { $this->queryError(odbc_errormsg(),$sql); } } else { $rsx = mysqli_query($this->conn, $this->sql_escape($sql)); if (!$rsx) { $this->queryError(@mysqli_error($this->conn),$sql); } } $this->rs = $rsx; } }
问题分析与解决方案
别慌,这个错误和你的INSERT语句本身无关,大概率是**数据库表的触发器(Trigger)**在搞鬼!
你仔细想想,request_boms表上是不是创建了触发器?比如当插入数据时,触发器里执行了包含子查询的SQL操作,而那个子查询返回了多行结果,才导致了这个错误。毕竟你的INSERT语句确实没有子查询,MySQL不可能平白无故抛出这个错误。
另外,也可以检查一下RecordSet类里的sql_escape方法(你代码里调用了它但没贴出来),会不会是这个方法在处理SQL时意外引入了子查询?不过这个可能性比较小,触发器的嫌疑最大。
给你几个验证和解决的步骤:
- 直接在数据库客户端执行INSERT语句:
复制你得到的那条SQL,在phpMyAdmin、Navicat或者MySQL命令行里直接执行:
如果同样报错,那100%是表上的触发器有问题。INSERT INTO request_boms SET request_list_id = 14, project_version_id = 429, amount = 1, user = 'admin', timestamp = 1607510083 - 查看表的触发器:
执行这条SQL查看request_boms表的所有触发器:
找到相关触发器后,检查里面的SQL逻辑,看看是不是有子查询返回了多行结果(比如用了SHOW TRIGGERS LIKE 'request_boms';=去匹配子查询的结果,但子查询返回了多条记录,这时候应该改用IN,或者调整子查询确保只返回一行)。 - 额外提醒:SQL注入风险:
你的代码用sprintf拼接SQL存在严重的SQL注入风险,建议改用预处理语句(Prepared Statements),比如mysqli的prepare和bind_param方法,既安全又能避免字符串拼接带来的各种奇怪问题。
内容的提问来源于stack exchange,提问作者Hayrettin Basaraner
相关产品推荐
相关产品推荐

