【问题标题】:ActiveRecord: Can successfully query with .where(foo: "bar") but .where.not(foo: "bar") doesn't return the correct resultsActiveRecord:可以使用 .where(foo: "bar") 成功查询,但 .where.not(foo: "bar") 不会返回正确的结果
【发布时间】:2017-07-11 19:37:22
【问题描述】:

我有一个这样构建的查询:

@events = current_organization.events.order(started_at: :asc)

我想查询所有status 不是"canceled" 的事件。但是,当我使用这样的查询时:

current_organization.events.order(started_at: :asc).where.not(status: "canceled")

它什么也不返回。但是,只是为了实验,我尝试了:

@events = current_organization.events.where(status: "canceled")

它成功返回了取消的事件。由于某种原因,反向不起作用。有什么原因吗?

编辑:我能找到的唯一解决方法就是使用where(status: nil),但这真的很不直观。

【问题讨论】:

  • 尝试检查#to_sql 输出。这有助于调试 sql 语句 current_organization.events.order(started_at: :asc).where.not(status: "canceled").to_sql
  • 您的事件模型上是否设置了default_scope
  • @MickeySheu,这里是不同的输出@events = current_organization.events.order(started_at: :asc).where(status: "canceled").to_sql=> "SELECT \"events\".* FROM \"events\" WHERE \"events\".\"organization_id\" = 1 AND \"events\".\"status\" = 'canceled' ORDER BY \"events\".\"started_at\" ASC”@events = current_organization.events.order(started_at: :asc).where.not(status: "canceled").to_sql=> “SELECT \"events\".* FROM \"events\" WHERE \"events\".\"organization_id\" = 1 AND (\"events\".\"status\" != 'canceled') ORDER BY \"events\".\"started_at\" ASC”
  • @mysmallidea,我的事件模型上没有设置默认范围
  • 您为什么希望您的where.not 查询能找到任何东西?您确定该组织有未取消的活动吗?

标签: ruby-on-rails ruby activerecord rails-activerecord ruby-on-rails-5


【解决方案1】:

对您的问题的更新:

编辑:我唯一能找到的解决方法就是使用where(status: nil),但这真的很不直观。

很重要。这告诉我您的status 列允许NULL 值,并且您有NULL 值。

那些NULLs 加上ActiveRecord 对where.not 的执行有些差,是您遇到麻烦的原因。如果我们查看生成的 SQL:

where.not(status: "canceled")

我们看到了:

("events"."status" != 'canceled') 

但在 SQL 中,x = nullx <> null 对于所有 x(包括当 x 本身为 null 时)都计算为 null,并且 null 在 SQL 中不是一个真实值;这意味着当涉及nulls 时,x <> yx = y 并不完全相反:如果一行的statusnull,那么where status = 'canceled'where status != 'canceled' 都不会找到它。

无论何时涉及nulls,您都必须与以您期望的方式对待null 的运营商合作:is nullis not nullis distinct fromis not distinct from、...

在您的 status 列中允许 nulls 对我来说听起来很奇怪,并且修复它会使问题消失:

  1. 添加迁移以将所有null 状态更改为更易于使用的状态。 status is null 列表示行/模型根本没有状态,这很奇怪,所以给它们一个真实的状态代码。
  2. 添加迁移以使您的status 列成为not null,这样您就不必再担心null 状态了。
  3. 更新您的模型的验证以不允许 status.nil? 发生。

一般来说,除非您确定 nulls 有意义并且您准备好处理 null 在 SQL 中的工作方式,否则不要在任何地方使用可为空的列。

【讨论】:

  • 为我的列设置默认值与使列不为空一样吗?
  • 并非如此,但您可以将其设置为 not null 并添加默认值。不过,您确实确实希望该列是 not null,否则 nulls 最终会出现在那里并导致问题。
  • 您的回答非常善于解释 SQL 如何处理 null。我继续对该专栏进行了必要的更改,我认为从现在开始不会给我带来问题。谢谢。
猜你喜欢
  • 2020-05-02
  • 2020-02-02
  • 1970-01-01
  • 2017-07-25
  • 1970-01-01
  • 2022-11-04
  • 2014-03-20
  • 1970-01-01
  • 2020-10-03
相关资源
最近更新 更多