SQL语句在phpMyAdmin正常运行但PHP脚本中报错的问题
解决PHP执行多语句SQL报错的问题
问题情况
我维护着一个存储运动员成绩的数据库,包含events(对应session)、sets、timetables、times、athletes表,各表通过唯一ID关联。需要实现传入session ID时,返回该session中至少有一条成绩记录的所有运动员。
编写的复合SQL在phpMyAdmin中能正常返回结果,但用PHP的$db->query($q)执行时返回false,报错提示SQL语法错误。调试发现生成的SQL和phpMyAdmin中的一致,仅缺少换行符,推测是单字符串包含多条查询语句导致的问题。
报错原因
PHP的query()方法默认不允许一次执行多条用分号分隔的SQL语句,你把SET @evID = ...;和后续的SELECT ...放在同一个字符串里执行,触发了这个限制,因此报错。
解决方案
方案1:分开执行两条SQL语句
将设置变量和查询的语句拆分,分别调用query()执行:
// 第一步:执行设置变量的语句 $setSql = "SET @evID = " . $method['sessID'] . ";"; $db->query($setSql); // 第二步:执行查询语句 $selectSql = "SELECT `athletes`.* FROM `events` INNER JOIN `sets` ON `sets`.`EventID` = `events`.`EventID` INNER JOIN `timetables` ON `timetables`.`SetID` = `sets`.`SetID` INNER JOIN `times` ON `times`.`TableID` = `timetables`.`TableID` INNER JOIN `athletes` ON `athletes`.`ID` = `times`.`AthleteID` WHERE `events`.`EventID` = @evID AND `times`.`TimeID` IN( SELECT MIN(`TimeID`) FROM `times` WHERE `TableID` IN( SELECT `TableID` FROM `timetables` WHERE `SetID` IN( SELECT `SetID` FROM `sets` WHERE `EventID` = @evID ) ) GROUP BY `AthleteID` )"; $result = $db->query($selectSql);
方案2:改写SQL去掉变量,用预处理语句更安全
直接将session ID代入查询语句,同时使用预处理语句(这是防止SQL注入的重要安全技巧):
// 准备预处理SQL,用?作为参数占位符 $stmt = $db->prepare("SELECT `athletes`.* FROM `events` INNER JOIN `sets` ON `sets`.`EventID` = `events`.`EventID` INNER JOIN `timetables` ON `timetables`.`SetID` = `sets`.`SetID` INNER JOIN `times` ON `times`.`TableID` = `timetables`.`TableID` INNER JOIN `athletes` ON `athletes`.`ID` = `times`.`AthleteID` WHERE `events`.`EventID` = ? AND `times`.`TimeID` IN( SELECT MIN(`TimeID`) FROM `times` WHERE `TableID` IN( SELECT `TableID` FROM `timetables` WHERE `SetID` IN( SELECT `SetID` FROM `sets` WHERE `EventID` = ? ) ) GROUP BY `AthleteID` )"); // 绑定参数,"i"表示参数是整数类型,传入session ID $stmt->bind_param("i", $method['sessID']); // 执行查询 $stmt->execute(); // 获取结果集 $result = $stmt->get_result();
额外优化:简化SQL逻辑
你的需求是获取“该session中至少有一条成绩的运动员”,无需嵌套多层子查询,用DISTINCT就能保证每个运动员仅返回一次,SQL可简化为:
$stmt = $db->prepare("SELECT DISTINCT `athletes`.* FROM `events` INNER JOIN `sets` ON `sets`.`EventID` = `events`.`EventID` INNER JOIN `timetables` ON `timetables`.`SetID` = `sets`.`SetID` INNER JOIN `times` ON `times`.`TableID` = `timetables`.`TableID` INNER JOIN `athletes` ON `athletes`.`ID` = `times`.`AthleteID` WHERE `events`.`EventID` = ?"); $stmt->bind_param("i", $method['sessID']); $stmt->execute(); $result = $stmt->get_result();
这个写法更简洁,执行效率也更高,完全能满足需求。
内容的提问来源于stack exchange,提问作者Ath.Bar.
相关产品推荐
相关产品推荐

