9

We'd like to write this query:

select * from table 
where col1 != 'blah' and col2 = 'something'

We want the query to include rows where col1 is null (and col2 = 'something'). Currently the query won't do this for the rows where col1 is null. Is the below query the best and fastest way?

select * from table 
where (col1 != 'blah' or col1 is null) and col2 = 'something'

Alternatively, we could if needed update all the col1 null values to empty strings. Would this be a better approach? Then our first query would work.


Update: Re: using NVL: I've read on another post that this is not considered a great option from a performance perspective.

Community
  • 1
  • 1
Marcus Leon
  • 55,199
  • 118
  • 297
  • 429
  • 1
    NVL: It all depends on how much data you are querying. You still have the option to do an index on a function if performance becomes a problem. – Josh Bush Mar 12 '09 at 00:31
  • We have quite a bit of data (100K rows). – Marcus Leon Mar 12 '09 at 00:37
  • But you are narrowing down on "col2", where an index can be used. How many rows do you have for a given value on "col2"? – Thilo Mar 12 '09 at 00:39
  • Most of the 100K rows have a value for col2 and col1 (about 20% of col1 rows are null). We present all this info to users and they have the ability to filter down the resultset. – Marcus Leon Mar 12 '09 at 00:42
  • We're using nvl on queries with 10mil+ records and are not having performance issues. Like Thilo said above, you can use other indexes to trim down before the nvl gets evaluated. – Josh Bush Mar 12 '09 at 00:42
  • FWIW, a reasonably standard SQL expression of your criteria would be `WHERE NULLIF(col1, 'blah') IS NOT NULL ...` This is likely to have the same performance implications as the NVL approach. Better would be `IS DISTINCT FROM`, but, as @BillKarwin notes, that is not supported in Oracle. – pilcrow Jul 25 '12 at 20:45
  • There'll be no significant performance difference between queries using `NVL(col1,'X')!='blah'` or `(col1!='blah' OR col1 IS NULL)`. – Jeffrey Kemp Jul 26 '12 at 03:08

9 Answers9

16

In Oracle, there is no difference between an empty string and NULL.

That is blatant disregard for the SQL standard, but there you go ...

In addition to that, you cannot compare against NULL (or not NULL) with the "normal" operators: "col1 = null" will not work, "col1 = '' " will not work, "col1 != null" will not work, you have to use "is null".

So, no, you cannot make this work any other way then "col 1 is null" or some variation on that (such as using nvl).

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    +1 to @Thilo's answer. Unfortunately, Oracle does not support the SQL-99 predicate "IS DISTINCT FROM" which would also be a solution. – Bill Karwin Mar 12 '09 at 00:38
3

While not the most readable - Oracle has an LNNVL Function that is essentially the not() function, but inverts the behavior for nulls. Meaning that comparing anything with null inside of lnnvl will return true (I don't know what performance implications this may have).

To do what you want in a single statement:

select * from table where lnnvl(col1 = 'blah') and col2 = 'something'

Note that this will only work for comparing a nullable value against a value you can be assured is non-nullable. Otherwise you'll need to do as Thilo suggests - use an operator similar to

lnnvl(nvl(col1, -1) = nvl(col2, -1))
  • I'm speechless. Sometimes I feel like Oracle is trolling everyone. That's a good function though, thanks. I'm just sorry to find myself in the position to need it. – Alan Hensley Feb 23 '15 at 21:47
3

I think that the solution that you posted is one of best options.

Regarding to performance, in my opinion it is not a big difference in this case, if the clause already have a != comparison usually the optimizer won't use an index in that column, because the selectivity is not enough, so the more discriminating filter will be the other side of the "and" condition.

If you ask me, I won't use an empty string as a null, but may be is just a personal preference.

Sam
  • 6,437
  • 6
  • 33
  • 41
2

It depends on your data, but most optimizers are going to look at col2 before col1, since = is an easier index than !=.

Otherwise, there are various ways you can speed this query up. It's probably best to do (col1 != 'blah' or col1 is null), but some database allow you to index a function. So you can index coalesce(col1, 0) and get good performance.

Really it depends on you data and your table.

Jeffrey Melloy
  • 421
  • 1
  • 3
  • 7
0

What about this option. I think it may work if your value is never null.

where not (value = column)

which would result in following truth table for evaluation for the where clause

                    col1
              | 'bla' |  null |
              -----------------
      | 'bla' |   F   |   T   |
value -------------------------
      |  null |   T   |  *T   | 

*this is the only one that's "wrong" but that's ok since our value is never null

Update

Ok, I just tried out my idea and it failed. I'll leave the answer here to save time of others trying the same thing. Here are my results:

select 'x', 'x' from dual where not ('x' = 'x');
0 rows
select 'x', 'y' from dual where not ('x' = 'y');
1 row
select 'x', 'null' from dual where not ('x' = null);
0 rows
select 'null', 'null' from dual where not (null = null);
0 rows

Update 2

This solution works if your value is never null (matches the truth table above)

where ('blah' != col1 or col1 is null)

tests here:

select 'x', 'x' from dual where ('x' != 'x' or 'x' is null);
0 rows
select 'x', 'y' from dual where ('x' != 'y' or 'y' is null);
1 row
select 'x', 'null' from dual where ('x' != null or null is null);
1 row
select 'null', 'null' from dual where (null != null or null is null);
1 row
Alex
  • 9,250
  • 11
  • 70
  • 81
  • Why are you looking for solutions that only work where the value can't be null? The whole point of the question was that the OP *needed* to include the nulls; and if you don't want to include them then the original query works. `or 'blah' is null` is by definition false, so it adds nothing to the query at all. I don't get where you're coming from; or why you're even revisiting this question really. – Alex Poole Jul 25 '12 at 21:09
  • I meant to say `or col1 is null`. fixed it. I approached the problem by attempting to simplify the query by removing the `is null` check but didn't succeed, so I left my tests in here for future reference in case anyone else gets the same ideas. – Alex Jul 27 '12 at 22:44
  • You've ended up where the OP started, so still not sure this is adding anything to the question. – Alex Poole Jul 28 '12 at 19:14
0

In oracle use the nvl function

select * from table where nvl(col1,'notblah') <> 'blah'
Josh Bush
  • 2,708
  • 4
  • 24
  • 25
0

If you want to speed up this sort of query, and you're on Oracle 10g or later, use a function-based index to turn those NULLs into values:

CREATE INDEX query_specific_index ON table (col2, NVL(col1,'***NULL***'));

select * from table 
where NVL(col1,'***NULL***') != 'blah' and col2 = 'something';

The database will quite likely use the index in this scenario (of course, subject to the decision of the CBO, affected by row counts and the accuracy of the statistics). The query MUST use the exact expression given in the index - in this case, "NVL(col1,'***NULL***')"

Of course, pick a value for '***NULL***' that will not conflict with any data in col1!

Jeffrey Kemp
  • 59,135
  • 14
  • 106
  • 158
-1

For Oracle

select * from table where nvl(col1, 'value') != 'blah' and col2 = 'something'

For SqlServer

select * from table where IsNull(col1, '') <> 'blah' and col2 = 'something'
MrTelly
  • 14,657
  • 1
  • 48
  • 81
-3

I think that your increase would be minimal in changing NULL values to "" strings. However if 'blah' is not null, then it should include NULL values.

EDIT: I guess I'm surprised why I got voted down here. If 'blah' if not null or an empty string, then it should never matter as you are already checking if COL1 is not equal to 'blah' which is NOT a NULL or an empty string.

Suroot
  • 4,315
  • 1
  • 22
  • 28