Rails 4原始SQL请求中的字符串插值

在没有插值的情况下重写此查询的最佳方法是什么?

def case_joins(type) subquery = <<-SQL.squish SELECT id FROM cases c2 WHERE c2.title_id = titles.id AND c2.value = 0 AND c2.type = '#{type}' ORDER BY c2.created_at DESC LIMIT 1 SQL "LEFT OUTER JOIN cases ON cases.title_id = titles.id AND cases.value = 0 AND cases.type = '#{type}' AND cases.id = (#{subquery})" end 

我假设您要避免插入变量,因为它对SQL注入开放是危险的。 我只是简单地加入从子查询中选择的案例,而不是将子查询放入WHERE条件中。 这确实涉及插值,但仅涉及AR生成的SQL。 我还将其实现为利用AR范围链的范围:

 class Title < ActiveRecord::Base def self.case_joins(type) case_query = Case.from("cases c").where(c: {title_id: title_id, value: 0, type: type}).order('c.created_at DESC').limit(1) joins("LEFT OUTER JOIN (#{case_query.to_sql}) cases ON cases.title_id = titles.id") end end 

这样,您可以将范围链接到其他人,如下所示:

 Title.where(attribute1: value1).case_joins("typeA") 

(注意,删除了外部SELECT多余的WHERE条件。)

很难推断出你的代码的其余部分是什么样的,但我认为在你的调用堆栈中进一步使用了titles

如果您使用ActiveRecord而不是本机SQL,则可以执行以下操作:

 def case_joins(scope, title_id, type) ids = Case.where(title_id: title_id, value: 0, type: type) .order('created_at desc').limit(1).pluck(:id) scope.joins('left outer join cases on cases.title_id = titles.id') .where(value: 0, type: type, id: ids) end 

这里的scope是您正在修改的当前AR查询。

这是我的头脑,所以我不确定上面的AR语法是否正确,但它确实避免了插入SQL并使用范围。

说实话,它并不比原生SQL更可读,因此YMMV。 它至少意味着(除了连接之外)你没有在代码中编码SQL。

以下是@ eirikir答案的修改,其作用与所讨论的方法相同。

 def case_joins(type) case_query = Case.from("cases c").where('c.title_id = titles.id AND c.value = 0 AND c.type = ?', type).order('c.created_at DESC').select(:id).limit(1) "LEFT OUTER JOIN cases ON cases.title_id = titles.id AND cases.id = (#{case_query.to_sql})" end