活动记录 - 如何在第二个表上执行嵌套选择?

时间:2017-05-02 14:09:11

标签: sql ruby-on-rails activerecord

我需要列出所有客户及其最新订单日期(加上分页)。

如何使用Active Record编写以下SQL查询?

select *,
  (
    select max(created_at)
    from orders
    where orders.customer_id = customers.id
  ) as latest_order_date
from customers
limit 25 offset 0

我试过了,但它抱怨missing FROM-clause entry for table "customers"

Customer
  .select('*')
  .select(
    Order
      .where('customer_id = customers.id')
      .maximum(:created_at)
  ).page(params[:page])

# Generates this (clearly only the Order query):
SELECT MAX("orders"."created_at") 
FROM "orders" 
WHERE (customer_id = customers.id)

编辑:保持AR的参数化和kaminari的分页优势会很好。

3 个答案:

答案 0 :(得分:0)

目前我提出的最积极的记录方式是:

data = [
  {
    "arrivalDate": 1493611200000,
    "price": 4588
  },
  {
    "arrivalDate": 1493352000000,
    "price": 4630
  },
  {
    "arrivalDate": 1493179200000,
    "price": 4553
  },
  {
    "arrivalDate": 1493092800000,
    "price": 4530
  },
  {
    "arrivalDate": 1493006400000,
    "price": 4578
  },
  {
    "arrivalDate": 1490572800000,
    "price": 4457
  }
]

df = pd.DataFrame(data)

我仍然希望我能使字符串部分更活跃记录。

Customer .page(params[:page]) .select('*') .select(<<-SQL.squish) ( SELECT MAX(created_at) AS latest_order_date FROM orders WHERE orders.customer_id = customers.id ) SQL 只是heredoc

答案 1 :(得分:0)

您尚未向我们提供有关这两个表格之间关系的任何信息,因此我将假设Customer has_many Orders

虽然ActiveRecord不支持您尝试执行的操作,但它建立在Arel之上,但确实如此。

每个Rails模型都有一个名为arel_table的方法,它将返回相应的Arel::Table。您可能希望a helper library使其更清晰,因为默认方式有点麻烦。我将使用普通的Arel语法来最大化兼容性。

ActiveRecord可以理解Arel对象,并且可以将它们与自己的语法一起接受。

orders = Order.arel_table
customers = Customer.arel_table

Customer.joins(:orders).group(:id).select([
  customers[Arel.star],
  orders[:created_at].maximum.as('latest_order_date')
])

哪个产生

SELECT "customers".*, MAX("orders"."created_at") AS "latest_order_date"
FROM "customers"
INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id"
GROUP BY "customers"."id"

这是执行此操作的惯例方式,但如果您仍想将其作为子查询执行,则可以执行此操作

Customer.select([
  customers[Arel.star],
  orders.project(orders[:created_at].maximum)
        .where(orders[:customer_id].eq(customers[:id]))
        .as('latest_order_date')
])

这给了我们

SELECT "customers".*, (
  SELECT MAX("orders"."created_at")
  FROM "orders"
  WHERE "orders"."customer_id" = "customers"."id" ) "latest_order_date"
FROM "customers"

答案 2 :(得分:0)

这是@adam给出的相同答案,但不是使用AREL而是直接使用ActiveRecord。不确定它真的比@JoãoMarceloSouza好多了

Customer.select("customers.*, max(orders.created_at)").joins(:orders).group("customers.id").page(params[:page])

(该组避免使用this feature of Postgres 9.1 and higher列出所有客户列。)

OP没有说,但查询不处理客户没有订单的情况。这个版本可以做到:

Customer.select("customers.*, coalesce(max(orders.created_at),0)").joins("left outer join orders on orders.customer_id=customers.id").group("customers.id").page(params[:page])