Search code examples
sqlruby-on-railsrubyactiverecord

Active record where.not returns empty array


Following command

Fight.last.fight_logs.where(item_id: nil)

generates sql:

Fight Load (0.3ms)  SELECT  "fights".* FROM "fights" ORDER BY "fights"."id" DESC LIMIT $1  [["LIMIT", 1]]
FightLog Load (0.2ms)  SELECT  "fight_logs".* FROM "fight_logs" WHERE "fight_logs"."fight_id" = $1 AND "fight_logs"."item_id" IS NULL LIMIT $2  [["fight_id", 27], ["LIMIT", 11]]

and returns:

#<ActiveRecord::AssociationRelation [#<FightLog id: 30, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 11.0, created_at: "2017-11-02 16:20:55", updated_at: "2017-11-02 16:20:57">, #<FightLog id: 31, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 3.0, created_at: "2017-11-02 16:20:57", updated_at: "2017-11-02 16:20:57">, #<FightLog id: 33, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 1.0, created_at: "2017-11-02 16:21:40", updated_at: "2017-11-02 16:21:40">, #<FightLog id: 32, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 7.0, created_at: "2017-11-02 16:21:33", updated_at: "2017-11-02 16:21:40">, #<FightLog id: 34, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 12.0, created_at: "2017-11-02 16:21:47", updated_at: "2017-11-02 16:21:48">, #<FightLog id: 35, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 14.0, created_at: "2017-11-02 16:21:48", updated_at: "2017-11-02 16:21:48">]>

but

Fight.last.fight_logs.where.not(item_id: 1)

generates sql:

Fight Load (1.0ms)  SELECT  "fights".* FROM "fights" ORDER BY "fights"."id" DESC LIMIT $1  [["LIMIT", 1]]
FightLog Load (0.8ms)  SELECT  "fight_logs".* FROM "fight_logs" WHERE "fight_logs"."fight_id" = $1 AND ("fight_logs"."item_id" != $2) LIMIT $3  [["fight_id", 27], ["item_id", 1], ["LIMIT", 11]]

and returns:

#<ActiveRecord::AssociationRelation []>

How it is possible? What i'm doing wrong?


Solution

  • You should specify NULL value in your query since you have it in your database:

    Fight.last.fight_logs.where('item_id != ? OR item_id IS NULL', 1)
    

    This is just how SQL works:

    select 1 != NULL;
    +-----------+
    | 1 != NULL |
    +-----------+
    |      NULL |
    +-----------+
    

    You can look at this answer to clarify the issue. Also, I would recommend avoiding using default NULL values in your database, there is nice answer about it. You can simply use default: 0, null: false your case.