MySQL SSH控制台子查询正常,mysqli返回子查询列0值问题
我之前也踩过类似MySQL用户变量在不同环境下行为不一致的坑,结合你的描述,咱们来梳理问题和解决办法:
问题核心
你的SQL查询在SSH直连MySQL时,第十列(guests)能正确返回0和1,但在PHP mysqli和PhpMyAdmin中,该列始终返回0,而其他列的结果在所有环境中都是正确的。
可能的原因
1. 会话变量@user_id未正确初始化
在SSH环境中,你可能之前手动执行过SET @user_id = 366;来初始化变量,但在mysqli或PhpMyAdmin的新会话中,这个变量并没有被赋值,默认是NULL。子查询中user_id = @user_id的条件会匹配不到任何数据,自然返回0。
2. 用户变量的执行顺序差异
MySQL中,SELECT子句里的变量赋值顺序和子查询的执行顺序可能在不同客户端有细微差别。你在SELECT中赋值@formal_id:=schedule.id,但子查询可能在这个赋值完成前就执行了,导致子查询中使用的@formal_id是之前会话遗留的NULL,从而返回0。
3. 多余的JOIN billing导致的隐性过滤
主查询中JOIN billing会过滤掉没有对应billing记录的tickets,但你说其他列结果一致,这个可能性相对较低,但仍值得注意。
解决方案
方案1:移除用户变量,改用关联子查询(推荐)
直接在子查询中关联主查询的字段,完全避免用户变量带来的问题,逻辑更清晰,也更可靠:
SELECT tickets.id, schedule.id as 'formal_id', schedule.name, schedule.date, schedule.colour, schedule.livers_in_price as 'li', schedule.livers_out_price as 'lo', schedule.guests_price as 'gu', tickets.for_sale as 'for_sale', -- 直接关联主查询的user.user_id和schedule.id,无需变量 (SELECT COUNT(*) FROM billing WHERE guest = 1 AND user_id = user.user_id AND formal_id = schedule.id) AS 'guests' FROM tickets JOIN user ON tickets.user_id = user.user_id JOIN schedule ON tickets.formal_id = schedule.id -- 移除多余的JOIN billing,避免不必要的数据过滤 WHERE user.user_id = 366 AND schedule.date > NOW() GROUP BY tickets.id;
方案2:提前初始化会话变量
如果一定要保留用户变量的写法,需要在执行主查询前先初始化@user_id:
- 在PHP mysqli中,先执行:
再执行你的主查询。$mysqli->query("SET @user_id = 366;"); - 在PhpMyAdmin中,先单独执行
SET @user_id = 366;,再运行主查询。
方案3:确保变量赋值顺序的可靠性
可以把变量赋值移到JOIN子句中,保证子查询执行前变量已经被正确赋值,但这个写法不如方案1直观:
SELECT tickets.id, @formal_id as 'formal_id', schedule.name, schedule.date, schedule.colour, @li_price as 'li', @lo_price as 'lo', @gu_price as 'gu', tickets.for_sale as 'for_sale', (SELECT COUNT(*) FROM billing WHERE guest = 1 AND user_id = @user_id AND formal_id = @formal_id) AS 'guests' FROM tickets JOIN user ON tickets.user_id = user.user_id JOIN schedule ON tickets.formal_id = schedule.id AND @formal_id := schedule.id AND @li_price := schedule.livers_in_price AND @lo_price := schedule.livers_out_price AND @gu_price := schedule.guests_price JOIN billing ON tickets.formal_id = billing.formal_id WHERE user.user_id = @user_id AND schedule.date > NOW() GROUP BY tickets.id;
执行前同样需要先初始化@user_id。
验证建议
优先测试方案1的查询,它完全规避了变量的问题,同时移除了多余的JOIN,能保证在所有客户端环境中返回一致的结果。
内容的提问来源于stack exchange,提问作者ABC Taylor

