【问题标题】:Rails 4 string interpolation in raw SQL request原始 SQL 请求中的 Rails 4 字符串插值
【发布时间】:2015-09-08 10:35:14
【问题描述】:

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

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

【问题讨论】:

    标签: ruby-on-rails ruby postgresql ruby-on-rails-4


    【解决方案1】:

    我假设您希望避免变量插值,因为它对 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条件。)

    【讨论】:

    • 这看起来不错,但是我需要为子查询中的表分配一个不同的名称 (case_query)。我可以使用WHERE 执行此操作吗?
    • @Pav31 我已经在子查询中使用别名编辑了答案——您只需要使用from 范围,然后编辑where 哈希以匹配。
    • 谢谢!它起作用了,尽管我需要通过在 limit(1)', and removing self 之前添加 select(:id) 来对您的方法进行小幅改动,因为我在模型之外声明了它。
    • 太好了,如果它对你有用,你能继续接受答案吗?
    【解决方案2】:

    很难推断出你的代码的其余部分是什么样的,但我认为 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。

    【讨论】:

    • 如果您使用.select(:id) 而不是.pluck(:id),那么到数据库服务器的往返只有一次而不是两次;如果你有一个非常大的数据集,这可能是你唯一的选择。
    【解决方案3】:

    这是对@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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多