I want to efficiently check if a table contains any rows that match <condition A> and do not match <condition B>, where the conditions are arbitrary.
In Oracle, this almost works:
select count(*) from dual
where exists (
select * from people
where (<condition A>)
and not (<condition B>)
);
-- returns zero if all rows that match <condition A> also match <condition B>
-- (well, almost)
The problem is the dreaded null values. Lets say <condition A> is name = 'Aaron' and <condition B> is age = 21. The query will correctly identify any Aarons whose age is not equal to 21, but it fails to identify any Aarons whose age is null.
Here is a correct solution, but on a table with millions of records it can take a while:
select (
select count(*) from people
where (<condition A>)
) - (
select count(*) from people
where (<condition A>)
and (<condition B>)
) from dual;
-- returns zero if all rows that match <condition A> also match <condition B>
-- (correct, but it is s l o w...)
Unfortunately the two conditions will be arbitrary, complex, changing, and generally out of my control. They are generated from the application's persistence framework from user searches, and while we try to keep our indexes up with our users, a lot of the time they will cause big table scans (which is why that first query with the "exists" clause is so much faster than the second - it can stop as soon as it finds one matching record, and it doesn't have to do two separate scans).
How can I do this efficiently without futzing up on the nulls?