当我进行两个应选择同一组人员的查询时,他们将得到不同的结果。逻辑相同,但数字不同。
这是我的模型:具有年龄的人类:整数名称:字符串宠物:字符串
class Person < ApplicationRecord
PET_TYPES = ['dog', 'cat', 'bird', 'fish']
validates :pet, inclusion: { in: PET_TYPES }, allow_nil: true
validates :age, numericality: { greater_than: 0 }
end
我已经在种子文件中填充了数据库,其中包含21岁以下或21岁以上的每种宠物(包括零)的人:
Person.all.destroy_all
Person::PET_TYPES.each do |pet|
10.times do |n|
Person.create!(name: "Person-young-#{pet}-#{n}", pet: pet, age: (1..20).to_a.sample)
end
10.times do |n|
Person.create!(name: "Person-old-#{pet}-#{n}", pet: pet, age: (21..80).to_a.sample)
end
end
10.times do |n|
Person.create!(name: "Person-young-no-pet-#{n}", pet: nil, age: (1..20).to_a.sample)
end
10.times do |n|
Person.create!(name: "Person-old-no-pet-#{n}", pet: nil, age: (21..80).to_a.sample)
end
我执行了以下两个查询,它们应该选择同一组人。但是我得到的数字不同。
Person.count
#>> (1.0ms) SELECT COUNT(*) FROM "people"
#=> 100
Person.where.not(id: Person.where.not('age > ? AND pet = ?', 21, 'dog')).count
#>> (1.7ms) SELECT COUNT(*) FROM "people" WHERE "people"."id" NOT IN (SELECT "people"."id" FROM "people" WHERE NOT (age > 21 AND pet = 'dog'))
#=> 20
Person.where('age > ? AND pet = ?', 21, 'dog').count
#>> (1.0ms) SELECT COUNT(*) FROM "people" WHERE (age > 21 AND pet = 'dog')
#=> 10
这两个语句是否返回相同的数字?
答案 0 :(得分:6)
在SQL中,null
不等于(=
)-甚至不等于其他null
。此外,这是Three-valued logic的常见示例。当您将null
与其他任何内容进行比较时,它不是是非不是;是UNKNOWN
。
(如更深入的讨论here所述),基本的“问题”是where.not(x: true)
是WHERE x != true
;它不是WHERE x == true
的否定形式。
让我们将您的问题分解为最小,完整且可验证的示例:
Person.count
(1.2ms) SELECT COUNT(*) FROM "people"
=> 100
Person.where(pet: 'dog').count
(1.0ms) SELECT COUNT(*) FROM "people" WHERE "people"."pet" = 'dog'
=> 20
Person.where.not(pet: 'dog').count
(1.4ms) SELECT COUNT(*) FROM "people" WHERE "people"."pet" != 'dog'
=> 60
Person.where.not(pet: 'dog').pluck(:pet).uniq
=> ["cat", "bird", "fish"]
如您所见,两个结果中都缺少带null
宠物的人。
总而言之,由于三值逻辑,您对查询进行双重否定的尝试实际上导致了不同的结果集。
Person.where.not('age > ? AND pet = ?', 21, 'dog')
包括除带狗的老人 和不带宠物的老人 以外的所有人。