I'm trying to clean up a change-set query that returns rows from a table that have changed since a given time and also do not exist in a known list of already previously returned results.
select ID, Field1, Field2
from Table1
where Field = 'Foo'
and (
ID not in ( %s )
or (
ID in ( %s ) and
GREATEST(created, COALESCE(modified, from_unixtime(0))) > from_unixtime(:stamp)
)
)
Now using some basic 9th grade logic operators….
A or (B and C) = (A or B) and (A or C)
Except in my instance, A = "id not in (%s)" and B = !A. C = the date constraint. Which reduces to…
(A or !A) and (A or C) => A or C
So, following that logic, my where clause should now look like this, but it seems funky. Can anybody more experienced with SQL and/or logic tell me if I've missed something simple?
select ID, Field1, Field2
from Table1
where Field1 = 'Foo'
and (
id not in ( %s )
or GREATEST(created, coalesce(modified, from_unixtime(0))) > from_unixtime(:stamp)
)
Does that look right?
Best Answer
Yes, it looks all right. (but only looks so)
Beware! SQL logic is a 3-valued logic, not the same as the "usual" math logic. The three values are
TRUE
,FALSE
andUNKNOWN
.In your case, if the
id
or the values in the( %s )
list containNULL
, the two versions are not equivalent. Example:5 NOT IN (6, 7, NULL)
and5 IN (6, 7, NULL)
will both evaluate toUNKNOWN
so no row will be returned by the 1st version. While the 2nd version will return the rows that satisfy the other conditiongreatest() ...
.If you have no Nulls, the two versions are equivalent.
You can find the truth tables in Wikipedia: Three-valued logic