You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IN子句的Prepared Statement查询MySQL8.0分区表无返回行问题

问题描述
  • 存在两张结构完全一致的表:t1为普通表,t2为按DAYOFMONTH(created_at)设置范围分区的分区表,两张表插入了完全相同的日期数据
  • 通过PHP MySQLi执行两条预编译查询语句:
    1. 使用IN子句的查询
    2. 使用OR多条件的查询
  • 查询结果出现差异:
    • t1的两条语句均返回2行目标数据
    • t2使用OR条件的查询返回2行数据,但使用IN子句的查询返回0行
  • 补充测试结果:
    • 当IN子句仅传入单个日期值时,t2的预编译查询可正常返回数据;传入多个值则失效
    • 使用MySQL CLI或MySQLi非预编译语句执行IN子句查询时,t2可正常返回结果
    • 尝试通过bind_param绑定多参数的方式,也无法解决该问题
  • 运行环境:MySQL 8.0.32、PHP 8.1.15、Fedora Linux 37
原因分析
  1. 分区修剪与预编译IN子句的兼容性问题:MySQL的分区修剪(Partition Pruning)逻辑在处理预编译语句中的多值IN子句时,无法正确解析绑定的参数组,导致无法匹配到存储目标数据的分区,最终返回空结果。
  2. 预编译参数解析逻辑差异:预编译语句中,IN子句的多参数会被MySQL作为整体参数处理,而基于DAYOFMONTH(created_at)的分区规则无法与这种参数形式联动,跳过了所有包含数据的分区;而非预编译语句或单值预编译语句中,MySQL能直接解析值并执行正确的分区筛选。
  3. 特定版本bug:该问题大概率是MySQL 8.0.32版本中,预编译语句与分区表交互的特定兼容性bug,在非预编译场景或更高版本中可能已修复。
解决方法
  • 改用OR条件替代IN子句:将IN ('2024-05-01', '2024-05-02')改写为created_at = '2024-05-01' OR created_at = '2024-05-02',利用OR条件能正常触发分区修剪的特性完成查询。
  • 动态生成安全的IN子句(需防注入):如果业务依赖IN语法,可在PHP中动态拼接包含占位符的IN子句,再通过bind_param绑定所有参数,示例代码:
    $dates = ['2024-05-01', '2024-05-02'];
    $placeholders = implode(',', array_fill(0, count($dates), '?'));
    $stmt = $mysqli->prepare("SELECT * FROM t2 WHERE created_at IN ($placeholders)");
    $types = str_repeat('s', count($dates));
    $stmt->bind_param($types, ...$dates);
    $stmt->execute();
    
    注:此方式需确保输入的日期值经过严格校验,避免SQL注入风险。
  • 调整分区策略:将分区键改为直接使用created_at(例如按月份或日期范围分区),而非基于DAYOFMONTH()的计算值,减少分区修剪与预编译语句的冲突。
  • 升级MySQL版本:尝试将MySQL升级到8.0.32之后的稳定版本,查看官方修复日志,确认是否已解决该预编译语句与分区表的交互bug。

内容的提问来源于stack exchange,提问作者Jens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:05:37