将带有row_number的SQL查询转换为AR代码

时间:2015-12-29 20:52:30

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

我有一些复杂的SQL查询需要转换为漂亮而干净的AR代码,而且我遇到了一些麻烦。

以下是查询:

SELECT a.*
FROM
  fixed_assets a
  INNER JOIN
    (
      SELECT e.id, e.fixed_asset_id
      FROM
        fixed_asset_book_entries e
      WHERE e.book_id = %SOME_VALUE_1%
    ) e_mod
    ON e_mod.fixed_asset_id = a.id
  INNER JOIN
    (
      SELECT s.fixed_asset_book_entry_id,
             s.status,
             ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
      FROM
        status_changes s
      WHERE s.created_at < %SOME_VALUE_2%
    ) s_mod
    ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;

所以,重点是提取这些fixed_assets行,这些行与某些fixed_asset_book_entries相关book_id,并且在特定日期之前有status_change行状态除inactive

我想要最终得到的是类级别(范围?)方法FixedAsset.active_within_book_on_date(book_id, date),它将返回FixedAsset对象,符合我上面解释的限制。我熟悉joins方法,但除了将原始SQL传递给row_number之外,我不知道如何处理joins函数。

1 个答案:

答案 0 :(得分:1)

我认为你能做的最好就是以下几点。在lib/sql_template.rb

class SqlTemplate
  attr_reader :sql
  # Load the file and process the ERB
  # Call it like this:
  #   sql = SqlTemplate.new(filename, binding)
  def initialize(filename, the_binding)
    raw_code = File.read(File.join(Rails.root, 'lib/sql', filename))
    template = ERB.new(raw_code)
    @sql     = template.result(the_binding)
  end
end

然后在lib/sql/active_within_book_on_date.sql中定义原始SQL。那将允许你这样做:

class FixedAsset
  def self.active_within_book_on_date(book_id, date)
    template = SqlTemplate.new('active_within_book_on_date.sql', binding)
    self.find_by_sql(template.sql)
  end
end

您的SQL文件如下所示:

SELECT a.*
FROM
  fixed_assets a
  INNER JOIN
    (
      SELECT e.id, e.fixed_asset_id
      FROM
        fixed_asset_book_entries e
      WHERE e.book_id = <%=book_id%>
    ) e_mod
    ON e_mod.fixed_asset_id = a.id
  INNER JOIN
    (
      SELECT s.fixed_asset_book_entry_id,
             s.status,
             ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
      FROM
        status_changes s
      WHERE s.created_at < '<%=date%>'
    ) s_mod
    ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;

这可能就像你能得到的那样'干净整洁'。