从所有订单状态为“错误”的订单中选择用户

时间:2017-07-31 02:47:22

标签: sql ruby-on-rails postgresql rails-console

很抱歉,如果这个问题令人困惑,因为我不确定如何提出问题,但我在这里:

我在rails控制台内试图找到所有订单所有订单状态为“错误”的用户,并且只有错误。用户有很多订单,每个订单的订单状态可能与“已完成”,“退款”或“错误”不同。

User.includes(:orders).where(orders: { state: "errored" })

这将返回所有具有一个或多个错误订单的用户,无论用户是否还有其他状态的订单。但我正在尝试提取错误订单的用户。

我已经尝试了很多东西,从迭代每个用户的每个订单,到尝试手动找到它们。但它必须是更好的方式。

3 个答案:

答案 0 :(得分:1)

我的SQL不像以前那样,但我相信对于纯SQL解决方案,它需要看起来像:

SELECT  "users".*
  FROM "users"
  LEFT JOIN orders on orders.user_id = users.id
  LEFT JOIN orders non_errored_orders on non_errored_orders.user_id = users.id and non_errored_orders.state <> 'errored'
  WHERE "non_errored_orders"."id" IS NULL AND "orders"."id" IS NOT NULL

因此,我们将orders表与别名non_errored_orders一起加入,这将使得如果存在用户ID匹配且状态不等于的订单错误,会出现一行(non_errored_orders.id最终会显示为NOT NULL)。在where子句中,我们会向下筛选仅non_errored_orders.id IS NULL的用户,过滤掉与错误订单匹配的所有用户。

然后我们再次加入orders表,只在users.id = orders.user_id上匹配别名。如果没有orders.id,则表示用户根本没有表中的任何订单,因此我们希望仅过滤到orders.user_id IS NOT NULL的用户,这意味着他们有订单。

您可以通过执行以下操作在rails中执行此类查询:

User.
  joins(:orders).
  joins("LEFT JOIN orders non_errored_orders on non_errored_orders.user_id = users.id and non_errored_orders.state <> 'errored'").
  where(non_errored_orders: { id: nil }).
  where.not(orders: { id: nil }).distinct
#  User Load (0.3ms)  SELECT  DISTINCT "users".* FROM "users" INNER JOIN "orders" ON "orders"."user_id" = "users"."id" LEFT JOIN orders non_errored_orders on non_errored_orders.user_id = users.id and non_errored_orders.state <> 'errored' WHERE "non_errored_orders"."id" IS NULL AND ("orders"."id" IS NOT NULL) LIMIT ?  [["LIMIT", 11]]
# => #<ActiveRecord::Relation [#<User id: 1, ...>]>

在我非常有限的测试集中,这似乎有效。

测试数据是

User.find(1).orders.create([ { state: 'errored' }, { state: 'errored' } ])
User.find(2).orders.create([ { state: 'errored' }, { state: 'completed' }])
User.find(3).orders.create([ { state: 'refunded' } ])

答案 1 :(得分:0)

那条漫长但简化的路径怎么样?

users_with_other_order_states = User.join(:orders).where.not(orders: { state: "errored" }).pluck(:id)

User.joins(:orders).where('orders.state = "errored" AND users.id NOT IN (?)', users_with_other_order_states)

答案 2 :(得分:-1)

我认为您可以尝试定义errored_orders关系:

class User < ApplicationRecord
  has_many errored_orders, ->{where state: 'errored'}
end

User.includes(:errored_orders).where(...)

希望它有所帮助!