【问题标题】:Selecting users from orders where all orders has the state "errored"从所有订单状态为“错误”的订单中选择用户
【发布时间】:2018-01-06 11:44:17
【问题描述】:

对不起,如果问题令人困惑,因为我不确定如何提问,但我开始:

我在 rails 控制台内试图查找所有用户的订单,其中所有订单的状态为“错误”,并且只有错误。一个用户有很多订单,每个订单上的订单状态可以是“已完成”、“已退款”或“错误”。

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

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

我尝试了很多东西,从遍历每个用户的每个订单到尝试手动找到它们。但它必须是一个更好的方法。

【问题讨论】:

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


    【解决方案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' } ])
    

    【讨论】:

    • 效果很好,在这里真的学到了一些新东西!非常感谢。
    【解决方案2】:

    小长但简单的路径怎么样?

    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)
    

    【讨论】:

    • 感谢您试一试。尝试使用您的简化路径时出现错误,我修复了拼写错误,但仍然无法正常工作。它给了我这个消息: ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "912" LINE 1: ...ERE (orders.state = "errored" AND users.id NOT IN 912,25,123 ...但无论如何,下面的答案之一得到了解决方案。所以无论如何感谢你试一试。
    • 刚刚更新了解决方案。该错误是由于 SQL 语法错误而出现的。将 ? 更改为 (?)
    【解决方案3】:

    我认为你可以尝试定义一个errored_orders 关系:

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

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-27
      • 1970-01-01
      • 2021-09-20
      • 2020-07-04
      • 1970-01-01
      相关资源
      最近更新 更多