I have a table of around 100 Users and I also have an array of user ids. What I wanted to do is show all users who are not a part of this array of user ids. When I do something like this
User.where('id NOT IN (?)', [9, 2, 3, 4])
It successfully returns the records where the user's id does not belong in that array. However if that array is empty like so
User.where('id NOT IN (?)', [])
It does not return any users back and the SQL query looks like this
SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
Does anyone know why this happens or could this be a bug? I am using Rails 3.2.5 with PostgreSQL.
ActiveRecord (3.2.1 at least) treats empty arrays as NULLs. The placeholders in a
where
call are handled bysanitize_sql
. If you trace through the code for a bit, you'll come toreplace_bind_variables
:and then
quote_bound_value
:An empty Array will satisfy all four conditions to get you to
c.quote(nil)
and that's where your NULL comes from. All the special logic that leads toc.quote(nil)
indicates that this is intentional behavior.Saying IN (or NOT IN) with an empty list:
should produce an SQL error so maybe the AR people are trying to prevent that by quietly turning that bad SQL into
c in (null)
. Note that neither of these:should ever produce any results due to the behavior of SQL's NULL. This is a classic newbie mistake and the AR people really should know better.
I'd prefer an exception myself: telling me that I'm about to deploy a foot-bullet would be much friendlier than just handing me a different gun.
Executive summary:
where('c in (?)', [])
orwhere('c not in (?)', [])
since neither statement makes much sense.Use ruby's active record wrapper:
This handles the empty array issue for you.
In Rails 4 you can use
User.where.not(id: [])
which will give you the correct result. It produces:Unfortunately
User.where('id NOT IN (?)', [])
should be equivalent but it is not. It still gives you the wrong result:References:
I don't know if this is the problem asked for, but I came here to find all records with an empty (serialized) array attribute. I solved it for Rails 5.0 like this:
Or for the inverse: