in列表过长会导致postgresql和mysql优化器选择率预估失准而放弃索引,应拆分为小批次union all或用values join替代;orm中字符串拼接易引发sql注入;pg函数内参数分布不均影响计划缓存;mysql预处理参数类型推断粗糙需显式转换。

SQL 查询中 IN 列表过长导致执行计划失效
PostgreSQL 和 MySQL 都会在 IN 子句元素超过一定数量(比如 PostgreSQL 默认 10000+)时放弃使用索引,退化为顺序扫描。这不是配置问题,是优化器对谓词选择率预估失准的典型表现。
- 用
EXPLAIN ANALYZE看执行计划,如果Seq Scan出现在本该走Index Scan的表上,大概率是IN太长触发了统计偏差 - 把大
IN拆成多个小批次(如每 500 个值一组),用UNION ALL拼接查询,比单条巨长语句快 3–10 倍 - MySQL 8.0.19+ 支持
VALUES ROW()构造临时数据集,配合JOIN替代IN,能稳定走索引 - 别依赖
SET SESSION work_mem来“撑”大IN,它不解决选择率误判,只影响排序/哈希内存
ORM 中参数化查询被拼接成字符串的隐性风险
像 Django 的 extra()、SQLAlchemy 的 text()、MyBatis 的 ${} 占位符,一旦混入用户输入,就等于绕过参数绑定,直接触发 SQL 注入——哪怕看起来用了“参数”。
- Django 中
filter(name__in=names)是安全的;但extra(where=["name IN %s"], params=[tuple(names)])在某些旧版本会降级为字符串格式化 - SQLAlchemy 使用
text("SELECT * FROM users WHERE id = :id")是 OK 的,但写成text(f"SELECT * FROM users WHERE id = {user_id}")就彻底失效 - MyBatis 里
#{}走 PreparedStatement,${}是字面量替换,连ORDER BY ${sortField}这种看似无害的用法也必须白名单校验
PostgreSQL 函数内传参与执行计划缓存冲突
PostgreSQL 对 PL/pgSQL 函数内 SQL 语句做“通用计划缓存”,但如果参数值分布极不均匀(比如一个值占 95% 查询量),缓存的执行计划很可能对多数调用次优。
修正说明:1,实现真正的软件开源。2,安装界面的美化3,真正实现栏目的递归无限极分类。4,后台添加幻灯片图片的管理,包括添加,修改,删除等。5,修正添加新闻的报错信息6,修正网站参数的logo上传问题7,修正产品图片的栏目无限极分类8,修正投票系统的只能单选问题9,添加生成静态页功能10,添加缓存功能特点和优势1. 基于B/S架构,通过本地电脑、局域网、互联网皆可使用,使得企业的管理与业务不受地域
- 在函数开头加
PERFORM pg_sleep(0)或RAISE DEBUG ''可强制跳过通用计划,改用每次重规划(代价是解析开销上升) - 更稳妥的是用
EXECUTE 'SELECT ...' USING param动态拼接,让优化器每次看到真实参数值再决策 - 注意:只有
STABLE或IMMUTABLE函数才可能被内联,VOLATILE函数不会,所以别指望函数体被展开优化
MySQL PREPARE + EXECUTE 的参数类型陷阱
MySQL 的预处理语句对参数类型推断非常粗糙,? 全部默认当成字符串处理,遇到数字比较、日期范围或 JSON 字段时容易隐式转换失败或索引失效。
-
WHERE created_at > ?传入字符串"2024-01-01"可能走索引;但传入整数时间戳1704067200就会触发created_at列的函数化转换,索引失效 - JSON 字段查询如
json_col->>'$.id' = ?,若传入数字而非字符串,MySQL 会先转成字符串再比,无法利用生成列上的索引 - 用
CONVERT(? USING utf8mb4)显式声明编码,避免因连接字符集不同导致参数被截断或乱码
参数传递看着只是填空,实际牵扯执行计划生成、类型推导、缓存策略三层逻辑。最容易被忽略的是:数据库不会因为你写了 ? 就自动理解你的业务语义,它只认类型、统计信息和 SQL 结构。










