How to write an ActiveRecord conditions containing null?

Code snippet:

conditions = {:org => ["ABC", "XYZ"]} Defect.count :conditions => conditions

work as expected.

This doesn't:

conditions = {:org => ["ABC", "XYZ", nil]} Defect.count :conditions => conditions

Get error: ActiveRecord::ActiveRecordError: Syntax error: Encountered "NULL" at line 1, column 87.: SELECT count(*) AS count_all FROM defects WHERE (defects.org IN ('ABC','XYZ',NULL))

Any idea?

SQL does not use the equality comparison for null values, it uses 'IS NULL' instead. Convenient, isn't it? :wink:

Apparently Rails' magic does not go as far as to check if the NIL value is one of the values in the array to generate the correct SQL statement so I think you'll have to do it yourself and generate a string such as:

conditions = "org is null or org in ('ABC', 'XYZ')"

This should be so easy to implement that I am not sure why it has not already been done.

pepe wrote:

SQL does not use the equality comparison for null values, it uses 'IS NULL' instead. Convenient, isn't it? :wink:

Apparently Rails' magic does not go as far as to check if the NIL value is one of the values in the array to generate the correct SQL statement so I think you'll have to do it yourself and generate a string such as:

conditions = "org is null or org in ('ABC', 'XYZ')"

This should be so easy to implement that I am not sure why it has not already been done.

Found a bug/workaround/fix. Not sure which.

conditions = {:org => ["ABC", "XYZ", "NULL"]} Defect.count :conditions => conditions

Don't know what to do if the org name really is "NULL".

John Duu wrote:

conditions = {:org => ["ABC", "XYZ", "NULL"]} Defect.count :conditions => conditions

Don't know what to do if the org name really is "NULL".

The string "NULL" is not the same as a NULL value in a field. I doubt what you're showing here will generate the proper SQL as shown earlier by pepe:

pepe wrote:

conditions = "org is null or org in ('ABC', 'XYZ')"

You need to actually check the generated SQL in your development log to make sure it looks something like the above.

Found a bug/workaround/fix. Not sure which.

conditions = {:org => ["ABC", "XYZ", "NULL"]} Defect.count :conditions => conditions

Just ran a quick test on a table of mine with 7 records (6 with the same value in the column being searched and one record with a value of null in it) and your code above does not work for me (using MSSQL). It just doesn't produce an error but the record with a NULL value is not counted. The only way I found to count the NULL, *and only the null*, with the syntax you've been using is this:

condition = {:my_column => nil}

Don't know what to do if the org name really is "NULL".

As far as I can see you will need to either modify your condition value or run 2 separate counts, one for the nil ones and another one for the other values. This code should help if you want to go with the first option, I think:

YourModel.count :conditions => "your_column is null or your_column in ('#{your_values.join('\',\'')}')"

This assumes 'your_values' is an array. The code above will probably not work, however, if 'your_values' is empty as the content of your parenthesis will end up looking like this: ('')

Then the query would select (if they exist) records with an empty value in 'your_column', which is something you might want to consider as well since 'your_column' contains strings. So something like this should work:

YourModel.count :conditions => "your_column is null or your_column = '' or your_column in ('#{your_values.join('\',\'')}')"

If there is a better way I'd like to know.

pepe wrote:

SQL does not use the equality comparison for null values, it uses 'IS NULL' instead. Convenient, isn't it? :wink:

Right, because NULL really means "unknown", and SQL uses 3-valued logic, so NULL == NULL actually returns NULL, not TRUE or FALSE -- that is, we don't know whether the two NULL values are "really" equal. (Unlike in Ruby, where nil and false are both false in a logical context, SQL NULL is not FALSE.)

So, if I remember my SQL correctly: NULL == TRUE yields FALSE NULL == FALSE yields FALSE NULL == NULL yields NULL which is why it's necessary to have the special IS NULL syntax: NULL IS NULL yields TRUE

Apparently Rails' magic does not go as far as to check if the NIL value is one of the values in the array to generate the correct SQL statement so I think you'll have to do it yourself and generate a string such as:

conditions = "org is null or org in ('ABC', 'XYZ')"

I think you're right. I don't see another good way to do it. I wonder, though, if COALESCE() would be useful in certain cases.

This should be so easy to implement that I am not sure why it has not already been done.

Interesting question. I've never needed this except as part of complex queries where I'm already writing SQL anyway, but YMMV.

Best,

> This should be so easy to implement that I am not sure why it has not > already been done.

Interesting question. I've never needed this except as part of complex queries where I'm already writing SQL anyway, but YMMV.

It occurred to me while working on my very first RoR gig. I 'inherited' a DB with a column for the status of the record where NULL and the empty value 'meant' the same thing and an asterisk (*) meant that the record was 'soft-deleted'. I ended up writing a lot of functionality just to get the correct records. It didn't help that I didn't know that much RoR at the time. Not that I know much more now... However I could see that the problem I was facing should be pretty common and was hoping to find an ActiveRecord solution, but I never did find it.

Robert Walker wrote:

John Duu wrote:

conditions = {:org => ["ABC", "XYZ", "NULL"]} Defect.count :conditions => conditions

Don't know what to do if the org name really is "NULL".

The string "NULL" is not the same as a NULL value in a field. I doubt what you're showing here will generate the proper SQL as shown earlier by pepe:

pepe wrote:

conditions = "org is null or org in ('ABC', 'XYZ')"

You need to actually check the generated SQL in your development log to make sure it looks something like the above.

Seem to be picking up nil correctly.

code snippet: conditions = {:org => ["NULL"]} defects = Defect.first :conditions => conditions puts defects.inspect

output: Defect Load (15.0ms) SELECT * FROM defects WHERE (defects.org IN ('NULL')) FETCH FIRST 1 ROWS ONLY

#<Defect id: 1, org: nil, created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

I'm using JRuby 1.5.1 with JavaDB. Haven't tried with mysql or other db.

Not too familiar with raw sql, so don't know if WHERE (defects.org IN ('NULL')) is equivalent to org IS NULL or if this syntax is a JavaDB specific.

John Duu wrote: [...]

Not too familiar with raw sql,

Then get familiar with it! You can't use ActiveRecord effectively without knowing SQL. It's an abstraction layer, not a crutch. You need to know what's being abstracted

so don't know if WHERE (defects.org IN ('NULL')) is equivalent to org IS NULL or if this syntax is a JavaDB specific.

It is not equivalent at all. See the apostrophes around 'NULL'? That means it's the literal string NULL, not the SQL null value.

If you knew SQL, this would be trivially self-evident. Please learn SQL.

Best,

Marnen Laibow-Koser wrote:

John Duu wrote: [...]

so don't know if WHERE (defects.org IN ('NULL')) is equivalent to org IS NULL or if this syntax is a JavaDB specific.

It is not equivalent at all. See the apostrophes around 'NULL'? That means it's the literal string NULL, not the SQL null value.

If you knew SQL, this would be trivially self-evident. Please learn SQL.

Best, -- Marnen Laibow-Koser http://www.marnen.org marnen@marnen.org

If WHERE (defects.org IN ('NULL')) select the string literal 'NULL', then the data selected is wrong, no?

I would expect #<Defect id: 1, org: "NULL", created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

but instead, I actually get

#<Defect id: 1, org: nil, created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

(org is string data type).

John Duu wrote:

Marnen Laibow-Koser wrote:

John Duu wrote: [...]

so don't know if WHERE (defects.org IN ('NULL')) is equivalent to org IS NULL or if this syntax is a JavaDB specific.

It is not equivalent at all. See the apostrophes around 'NULL'? That means it's the literal string NULL, not the SQL null value.

If you knew SQL, this would be trivially self-evident. Please learn SQL.

Best, -- Marnen Laibow-Koser http://www.marnen.org marnen@marnen.org

If WHERE (defects.org IN ('NULL')) select the string literal 'NULL', then the data selected is wrong, no?

I would expect #<Defect id: 1, org: "NULL", created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

but instead, I actually get

#<Defect id: 1, org: nil, created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

(org is string data type).

Hmm. I wonder if JavaDB stores null values as the literal "NULL", or if the Ruby/JavaDB adaptor is at fault.

In either case, your database (or its adaptor) is doing something silly (in that there is apparently no way to distinguish between NULL and "NULL"), and you should certainly not rely on this silliness.

Best,

If WHERE (defects.org IN ('NULL')) select the string literal 'NULL', then the data selected is wrong, no?

I would expect #<Defect id: 1, org: "NULL", created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

but instead, I actually get

#<Defect id: 1, org: nil, created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

(org is string data type).

Hmm. I wonder if JavaDB stores null values as the literal "NULL", or if the Ruby/JavaDB adaptor is at fault.

In either case, your database (or its adaptor) is doing something silly (in that there is apparently no way to distinguish between NULL and "NULL"), and you should certainly not rely on this silliness.

Best, -- Marnen Laibow-Koser http://www.marnen.org marnen@marnen.org

I set up a test using sql3lite & mysql. Both responded the same way.

Look like 'NULL' in the IN clause is a reserve word. For a string literal NULL, you'll have to quote it.

So WHERE (defects.org IN ('NULL')) selects nil

and WHERE (defects.org in ('"NULL"')) select the literal "NULL".

John Duu wrote: [...]

I set up a test using sql3lite & mysql. Both responded the same way.

Can I see your test code?

Look like 'NULL' in the IN clause is a reserve word. For a string literal NULL, you'll have to quote it.

So WHERE (defects.org IN ('NULL')) selects nil

and WHERE (defects.org in ('"NULL"')) select the literal "NULL".

I'd be surprised. '"NULL"' should represent a 6-character string beginning and ending with a double-quote character, not the 4-character string just consisting of N, U, L, L.

Best,

If WHERE (defects.org IN ('NULL')) select the string literal 'NULL', then the data selected is wrong, no?

I would expect #<Defect id: 1, org: "NULL", created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

but instead, I actually get

#<Defect id: 1, org: nil, created_at: "2010-10-04 00:14:20", updated_at: "2010-10-04 00:14:20>

(org is string data type).

Hmm. I wonder if JavaDB stores null values as the literal "NULL", or if the Ruby/JavaDB adaptor is at fault.

In either case, your database (or its adaptor) is doing something silly (in that there is apparently no way to distinguish between NULL and "NULL"), and you should certainly not rely on this silliness.

Best, -- Marnen Laibow-Koser http://www.marnen.org marnen@marnen.org

I set up a test using sql3lite & mysql. Both responded the same way.

Look like 'NULL' in the IN clause is a reserve word. For a string literal NULL, you'll have to quote it.

So WHERE (defects.org IN ('NULL')) selects nil

and WHERE (defects.org in ('"NULL"')) select the literal "NULL".

Not for me... osx, mysql 5.1.46..... sounds like your adapter is doing something it shouldn't be...