Active Record Where Not boolean: true

Ric*_*ich 3 ruby activerecord ruby-on-rails

I'm struggling to wrap my mind around an ActiveRecord query.

I'm trying to search my database for GolfRetailer objects with ID's 1..100, that have something (not nil) in their :website field, and that don't have true in their duplicate_domain field.

Here's the query I expected to work:

GolfRetailer.where.not(website: nil, duplicate_domain: true).where(id: 1..100)

I also tried this variant of essentially the same query: GolfRetailer.where.not(website: nil).where(id: 1..100, duplicate_domain: !true)

But both return an empty array, despite there definitely being records that meet those requirements.

When I run GolfRetailer.where.not(website: nil).where(id: 1..100) I get an array, and when I run GolfRetailer.where.not(website: nil, duplicate_domain: nil).where(id: 1..100) I also get an array, but with all records that do have the true duplicate_domain flag, which isn't what I'm looking for.

I'd rather not search for records that have duplicate_domain: nil as that's not always correct (I may not have processed their domain yet).

For clarity, here is the Schema for the Model.

create_table "golf_retailers", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "place_id"
    t.string "website"
    t.string "formatted_address"
    t.string "google_places_name"
    t.string "email"
    t.boolean "duplicate_domain"
    t.index ["duplicate_domain"], name: "index_golf_retailers_on_duplicate_domain"
  end
Run Code Online (Sandbox Code Playgroud)

What am I missing to make this query work?

Sco*_*ell 5

发生这种情况是因为在SQL中执行a时!= TRUE,NULL结果中将不包含任何值。这是因为该NULL值表示一个未知值,所以DB不知道如何对未知值执行任何比较操作,因此将它们排除在外。

解决此问题的一种方法是使用IS DISTINCT FROM:

GolfRetailer
  .where(id: 1..100)
  .where.not(website: nil)
  .where("duplicate_domain IS DISTINCT FROM ?", true)
Run Code Online (Sandbox Code Playgroud)

正如其他人提到的那样,您还应该问自己,如果a确实GolfRetailer有一个,是否真的不为您所知duplicate_domain。

如果,所有GolfRetailer符合S duplicate_domain的NULL实际上意味着他们没有一个(false)比你应该考虑防止NULL值该列完全。

您可以通过在change_column数据库迁移的列上添加NOT NULL约束来实现。

为了添加NOT NULL约束,您首先需要确保该列中的所有数据都具有非null值。

def change
  GolfRetailer.in_batches.update_all(duplicate_domain: false)

  change_column_null :golf_retailers, :duplicate_domain
end

Run Code Online (Sandbox Code Playgroud)

如果您的应用程序处于负载状态,则还应注意此类迁移可能具有的潜在性能-特别是如果您添加具有默认值的NOT NULL约束。

考虑使用诸如Strong Migrations gem之类的工具来帮助查找可能导致生产前停机的数据库迁移。