NOT IN and NULL
October 31st, 2007
I posted about Unexpected results in June 2005. Here are more results from NOT IN and NULL
select * from dual
WHERE (1) NOT IN (SELECT NULL FROM DUAL);
no rows selected
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,1 FROM DUAL);
no rows selected
However, and this surprised me,
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,2 FROM DUAL);
D
-
X
Probably the expression
where (a,b) not in (select c,d from t)
is translated into
where (a!=c or b!=d) — first row of t
and (a!=c or b!=d) — second row of t
– and …
Any comparison between null and another value will return false, unless the operators are used is null or is not null
the point is :
(1,1) NOT IN (SELECT NULL,2 FROM DUAL);
returns TRUE
also note that Any comparison between null and another value will return false is inaccurate.
My 2-cent is that any comparison with NULL actually does not yield anything simply because it could not be done. A NULL (not defined) cannot be compared or calculated. I see NULL as a state. That’s why it can be only be used with IS or IS NOT.
Laurent, I agree with your assumption. Since the first comparison a!=c is not comparable due to value NULL is involved, so, I assume, it is ignored. However, the second comparison b!=d return TRUE, and that counts.
Thanks for provoking our thinking.
Running it through explain plan comes up with interesting filter predicates (10.2.0.3):
1 - filter( NOT EXISTS (SELECT /*+ */ 0 FROM “DUAL” “DUAL” WHERE
NULL IS NOT NULL))
3 - filter(NULL IS NOT NULL)
Thanks for the hint of looking at the explain plan filter.
If you try with something else than litteral, you would get a LNNVL
one more plan
select * from dual
WHERE (1) NOT IN (SELECT NULL FROM DUAL);
(1) NOT IN (SELECT NULL FROM DUAL)
NOT ( 1 IN (SELECT NULL FROM DUAL))
NOT (1=NULL)
NOT (NULL)
NULL
So return now rows as only True returning rows.
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,1 FROM DUAL);
(1,1) NOT IN (SELECT NULL,1 FROM DUAL)
NOT ((1,1) IN (SELECT NULL,1 FROM DUAL))
NOT (1=NULL AND 1=1)
NOT (NULL AND TRUE)
NOT (NULL)
NULL
So return now rows as only True returning rows.
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,2 FROM DUAL);
(1,1) NOT IN (SELECT NULL,2 FROM DUAL)
NOT ((1,1) IN (SELECT NULL,2 FROM DUAL))
NOT (1=NULL AND 1=2)
NOT (NULL AND FALSE)
NOT (FALSE)
TRUE
So return rows.
The above analysis is based on the fact that:
NULL AND TRUE = NULL (1)
NULL AND FALSE=FALSE (2)
NULL OR TRUE=TRUE (3)
NULL OR FALSE=NULL (4)
Why is that?
For ‘AND’ condition, the whole expression is TRUE only if all of them are TRUE, otherwise if any of them is FALSE, the whole expression will be FALSE, otherwise is NULL.
So, TRUE AND TRUE AND TRUE = TRUE
TRUE AND FALSE AND NULL = FALSE
TRUE AND NULL AND FALSE = FALSE
TRUE AND NULL AND NULL = NULL
Examples:
/*TRUE AND TRUE AND TRUE = TRUE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=1) AND (1=1);
–return 1 row
/*TRUE AND FALSE AND NULL = FALSE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=2) AND (1=NULL);
–return no row
/*Not(TRUE AND FALSE AND NULL) = NOT (FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=2) AND (1=NULL));
–return 1 row
/*TRUE AND NULL AND FALSE = FALSE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=null) AND (1=2);
–return no row
/*NOT(TRUE AND NULL AND FALSE) = NOT(FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=null) AND (1=2));
–return 1 row
/*NOT(TRUE AND NULL AND NULL) = NOT(NULL)=NULL*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=null) AND (1=null));
–return no row
For ‘OR’ condition, the whole expression is FALSE if any of them is FALSE, otherwise if all of them is TRUE, the whole expression is TRUE, otherwise is NULL.
So, TRUE OR FALSE OR NULL =FALSE
FALSE OR TRUE OR NULL = FALSE
TRUE OR NULL OR TRUE = NULL
TRUE OR TRUE OR TRUE = TRUE
For examples:
/*FALSE OR FALSE OR FALSE = FALSE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=2) OR (1=2);
–return no row
/*NOT(FALSE OR FALSE OR FALSE) = NOT(FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=2) OR (1=2) OR (1=2));
–return 1 row
/*FALSE OR TRUE OR NULL = TRUE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=1) OR (1=NULL);
–return 1 row
/*FALSE OR NULL OR TRUE = TRUE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=NULL) OR (1=1);
–return 1 row
/*FALSE OR NULL OR NULL = NULL*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=null) OR (1=null);
–return no row
/*NOT(FALSE OR NULL OR NULL) = NOT(NULL)=NULL*/
SELECT * FROM DUAL
WHERE NOT((1=2) OR (1=null) OR (1=null));
–return no row
Why is that? Because
Please ignore previous post, this one is correct
———————————————–
select * from dual
WHERE (1) NOT IN (SELECT NULL FROM DUAL);
/*(1) NOT IN (SELECT NULL FROM DUAL)
NOT ( 1 IN (SELECT NULL FROM DUAL))
NOT (1=NULL)
NOT (NULL)
NULL
So return now rows as only True returning rows.*/
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,1 FROM DUAL);
/*(1,1) NOT IN (SELECT NULL,1 FROM DUAL)
NOT ((1,1) IN (SELECT NULL,1 FROM DUAL))
NOT (1=NULL AND 1=1)
NOT (NULL AND TRUE)
NOT (NULL)
NULL
So return now rows as only True returning rows.*/
select * from dual
WHERE (1,1) NOT IN (SELECT NULL,2 FROM DUAL);
/*(1,1) NOT IN (SELECT NULL,2 FROM DUAL)
NOT ((1,1) IN (SELECT NULL,2 FROM DUAL))
NOT (1=NULL AND 1=2)
NOT (NULL AND FALSE)
NOT (FALSE)
TRUE
So return rows.*/
/*The above analysis is based on the fact that:
NULL AND TRUE = NULL (1)
NULL AND FALSE=FALSE (2)
NULL OR TRUE=TRUE (3)
NULL OR FALSE=NULL (4)*/
/*Why is that? Becuase:
For ‘AND’ condition, the whole expression is TRUE only if all of them are TRUE,
otherwise if any of them is FALSE, the whole expression will be FALSE, otherwise is NULL.
TRUE AND TRUE AND TRUE = TRUE
TRUE AND FALSE AND NULL = FALSE
TRUE AND NULL AND FALSE = FALSE
TRUE AND NULL AND NULL = NULL*/
–Examples:
/*TRUE AND TRUE AND TRUE = TRUE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=1) AND (1=1);
–return 1 row
/*TRUE AND FALSE AND NULL = FALSE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=2) AND (1=NULL);
–return no row
/*Not(TRUE AND FALSE AND NULL) = NOT (FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=2) AND (1=NULL));
–return 1 row
/*TRUE AND NULL AND FALSE = FALSE*/
SELECT * FROM DUAL
WHERE (1=1) AND (1=null) AND (1=2);
–return no row
/*NOT(TRUE AND NULL AND FALSE) = NOT(FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=null) AND (1=2));
–return 1 row
/*NOT(TRUE AND NULL AND NULL) = NOT(NULL)=NULL*/
SELECT * FROM DUAL
WHERE NOT((1=1) AND (1=null) AND (1=null));
–return no row
/*For ‘OR’ condition, the whole expression is FALSE if all of them are FALSE,
otherwise if any of them is TRUE, the whole expression is TRUE, otherwise is NULL.
FALSE OR FALSE OR FALSE = FALSE
FALSE OR TRUE OR NULL = TRUE
FALSE OR NULL OR TRUE = TRUE
FALSE OR NULL OR NULL = NULL*/
–For examples:
/*FALSE OR FALSE OR FALSE = FALSE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=2) OR (1=2);
–return no row
/*NOT(FALSE OR FALSE OR FALSE) = NOT(FALSE)=TRUE*/
SELECT * FROM DUAL
WHERE NOT((1=2) OR (1=2) OR (1=2));
–return 1 row
/*FALSE OR TRUE OR NULL = TRUE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=1) OR (1=NULL);
–return 1 row
/*FALSE OR NULL OR TRUE = TRUE*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=NULL) OR (1=1);
–return 1 row
/*FALSE OR NULL OR NULL = NULL*/
SELECT * FROM DUAL
WHERE (1=2) OR (1=null) OR (1=null);
–return no row
/*NOT(FALSE OR NULL OR NULL) = NOT(NULL)=NULL*/
SELECT * FROM DUAL
WHERE NOT((1=2) OR (1=null) OR (1=null));
–return no row