如何将Squeel gem编写的DB查询改写为正确的Active Record语句
重写代码的核心错误
- 字段归属错误:
parent_vendor_id是vendors表的字段,你错误写在了projects表的查询条件里,导致关联供应商的条件完全没有生效。 - 条件逻辑优先级错误:原始逻辑是
A OR B OR (C AND D),你的写法变成了(A OR B OR C) AND D,相当于所有返回结果都必须满足认证已发布,完全不符合原始需求。 - ActiveRecord查询语法错误:直接写
.or(certifications.donaldson == true)是完全错误的写法,这里的==是Ruby语法,会直接返回布尔值,不会被解析为SQL查询条件,相当于这部分条件完全失效。 - 关联类型错误:第二次重写把
left_outer_joins(:certifications)改成了内连接joins(:certifications),会直接过滤掉没有关联认证的项目,和原始Squeel使用外连接保留无认证项目的逻辑冲突。
正确实现方案
推荐使用带绑定变量的原生SQL片段实现,逻辑清晰且不易出错:
if donaldson_primary? Project.joins(:vendor) .left_outer_joins(:certifications) .where("projects.vendor_id = :vendor_id OR vendors.parent_vendor_id = :vendor_id OR (certifications.donaldson = TRUE AND certifications.published = TRUE)", vendor_id: vendor_id) .distinct elsif cdti_primary? Project.joins(:vendor) .left_outer_joins(:certifications) .where("projects.vendor_id = :vendor_id OR vendors.parent_vendor_id = :vendor_id OR (certifications.cdti = TRUE AND certifications.published = TRUE)", vendor_id: vendor_id) .distinct end
方案说明
- 关联逻辑完全对齐原始Squeel:内连接
vendor保证项目一定有关联供应商,左外连接certifications保留无认证的项目,只要满足前两个条件即可返回。 - 用绑定变量传递参数,不存在SQL注入风险,同时完美匹配原始逻辑的条件优先级,不需要处理ActiveRecord
or方法要求两边查询结构一致的限制。 - 代码简洁可读性高,后续维护成本远低于硬凑ActiveRecord DSL的写法。
关于原生SQL方案的疑问解答
只要使用绑定变量传递参数,不要直接拼接用户输入的字符串,where(原生SQL片段)的方案完全没有不良影响,对于多表关联的复杂条件,反而比硬凑ActiveRecord DSL更不容易出错。
内容的提问来源于stack exchange,提问作者Scott Milella
相关产品推荐
相关产品推荐

