简体   繁体   English

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

[英]Rails 4 string interpolation in raw SQL request

What would be the best way of rewriting this query without interpolation? 在没有插值的情况下重写此查询的最佳方法是什么?

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

I'm assuming that you want to avoid interpolation of variables, which is dangerous since its open to SQL injection. 我假设您要避免插入变量,因为它对SQL注入开放是危险的。 I would simply join onto the cases selected from the subquery instead of putting the subquery into the WHERE conditions. 我只是简单地加入从子查询中选择的案例,而不是将子查询放入WHERE条件中。 This does involve interpolation, but only of AR-generated SQL. 这确实涉及插值,但仅涉及AR生成的SQL。 I would also implement it as a scope to leverage AR scope chaining: 我还将它作为一个范围来实现,以利用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

This way, you can chain the scope to others like so: 这样,您可以将范围链接到其他人,如下所示:

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

(Note that removed the superfluous WHERE conditions in the outer SELECT .) (注意,删除了外部SELECT多余的WHERE条件。)

It's difficult to infer what the rest of your code looks like, but I presume titles is being used in a query further up your call stack. 很难推断出你的代码的其余部分是什么样的,但是我认为在你的调用堆栈中进一步使用了titles

If you were to use ActiveRecord instead of native SQL, you could do something like this: 如果您使用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 here is the current AR query you are modifying. 这里的scope是您正在修改的当前AR查询。

This is off the top of my head, so I'm not sure if the AR syntax above is correct, but it does avoid the need to interpolate SQL and also uses scoping. 这是我的头脑,所以我不确定上面的AR语法是否正确,但它确实避免了插入SQL并使用范围。

To be honest, though, it's not all that much more readable than native SQL, and so YMMV. 说实话,它并不比原生SQL更可读,因此YMMV。 It does at least mean that (apart from the join) you're not encoding SQL in your code. 它至少意味着(除了连接之外)你没有在代码中编码SQL。

Here is modification of @eirikir's answer, that works the same way as method in question. 以下是@ 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM