【问题标题】:rails find_by returns incorrect record?rails find_by 返回不正确的记录?
【发布时间】:2019-01-24 04:37:35
【问题描述】:

我有一个应用程序,其中User 创建一个Transaction 以从不同的User 购买Item。我突然在Item 的一种方法中遇到了find_by 的困难。我想找到第一个 Transaction 涉及调用它的 Item,并且我想通过搜索一些无效状态来进一步限制该结果。

class Item < ApplicationRecord
 def first_find
  Transaction.find_by("item_id = ? AND recipient_id = ? AND state != ? OR state != ? OR state != ?", self.id, author.id, :ignored, :declined, :unapproved)
 end
end

无论如何,它的作用是返回我数据库中的第一个Transaction。这不是预期的行为。所以在控制台中,如果我像t = Transaction.last 那样缓存Transaction id #5(其中item_id 为7,recipient_id 为4),然后调用t.item.first_find,我大概会得到Transaction #5。此查询的 SQL 输出为 Transaction Load (1.1ms) SELECT "transactions".* FROM "transactions" WHERE (item_id = 7 AND recipient_id = 4 AND state != 'ignored' OR state != 'declined' OR state != 'unapproved') LIMIT $1 [["LIMIT", 1]]

这太棒了!这就是我想要的输出。但令我困惑的是,它返回:

#<Transaction id: 2, sender_id: 1, recipient_id: 2, item_id: 9 ..... >

有人知道为什么吗?谢谢!


编辑 1

所以我想我已经解决了?我之前遇到过这个问题,在 where 子句中放入太多搜索参数会因为某种原因搞砸。

所以虽然这工作

Transaction.find_by("item_id = ? AND recipient_id = ? AND state != ? OR state != ? OR state != ?", self.id, author.id, :ignored, :declined, :unapproved)

这样

Transaction.where("item_id = ? AND recipient_id = ?", self.id, author.id).where("state != ? OR state != ? OR state != ?", :ignored, :declined, :unapproved).first

不过,我不完全确定为什么。有人知道吗?


编辑 2

AND 运算符应与 OR 运算符分开。

【问题讨论】:

  • 您是否在 SQL 控制台中尝试过相同的查询?
  • @BradWerth SQL 控制台是什么?我已经在 Rails 控制台中尝试过,如果这就是你的意思
  • 如果您使用的是 mongoid,您不能期望事先订购。它完全随机。您需要手动定义排序和限制1
  • @Oshanz 我正在使用 postgresql
  • 另外我认为你需要将“or”条件与“and”s ex分开:a and b and (c or d or e)

标签: ruby-on-rails


【解决方案1】:

回答为什么。 这就是 SQL operator precedence 的工作原理。更多解释是here。因此,当您将其分解为 builds a new relation 的另一个“where”子句时,这是根据参数中的条件过滤当前关系的结果。 source code is here

让我展示其他解决方案。

1.

    Transaction.where(item_id: self.id, recipient_id: author.id).where.not(state: [:ignored, :declined, :unapproved]).first

2.

recipient_transactions = Transaction.where(item_id: self.id, recipient_id: author.id)
active_transactions = Transaction.where.not(state: [:ignored, :declined, :unapproved])
result = recipient_transactions.merge(active_transactions).first # this buils a single query

【讨论】:

    【解决方案2】:

    我认为你应该使用where 子句而不是使用find_by

    class Item < ApplicationRecord
      def first_find
        Transaction.where("item_id = ? AND recipient_id = ? AND state != ? OR state != ? OR state != ?", self.id, author.id, :ignored, :declined, :unapproved)
      end
    end
    

    如果您使用 find 语句,这将返回 ActiveRecord::Relation(record collections) 而不仅仅是一条记录

    【讨论】:

    • 我的目标是找到满足这些条件的第一个记录,而不是集合。我想我可以做到Transaction.where(bla bla).first,但两者仍然会产生同样令人困惑和意想不到的结果。
    • 您可以使用多个or, and where 子句,因为查询中有一些运算符优先级。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多