问题
Is there a way to write a prepared statement where a value is compared to another value in a condition and I don't know, whether this value is NULL
or not.
SELECT `foo` FROM `bar` WHERE `a1` = :a1 AND `a2` = :a2
If I would use this prepared statement with a1 => null
and a2 => 42
, then the resulting query would be:
SELECT `foo` FROM `bar` WHERE `a1` = NULL AND `a2` = '42'
This is, of course, not what I want. I would need this in that case:
SELECT `foo` FROM `bar` WHERE `a1` IS NULL AND `a2` = '42'
^^
Both a1
and a2
are nullable. I don't want to define 4 prepare statements:
-- I would use this, if both values are not null
SELECT `foo` FROM `bar` WHERE `a1` = :a1 AND `a2` = :a2
-- and this, if the expected value of a1 is null
SELECT `foo` FROM `bar` WHERE `a1` IS NULL AND `a2` = :a2
-- and this, if the expected value of a2 is null
SELECT `foo` FROM `bar` WHERE `a1` = :a1 AND `a2` IS NULL
-- and this, if I would expect both values to be null
SELECT `foo` FROM `bar` WHERE `a1` IS NULL AND `a2` IS NULL
回答1:
MySQL provides a null-safe comparison <=>
(spaceship) operator. That specifies an equality comparison that will return TRUE or FALSE, and won't return NULL when either of the operands is NULL.
As a demonstration:
SELECT NULL=NULL
, NULL<=>NULL
, 1=NULL
, 1<=>NULL
, 1=0
, 1<=>0
, 1=1
, 1<=>1
Returns:
NULL=NULL NULL<=>NULL 1=NULL 1<=>NULL 1=0 1<=>0 1=1 1<=>1
--------- ----------- ------ -------- ------ ----- ------ -----
(NULL) 1 (NULL) 0 0 0 1 1
That comparison operation is essentially shorthand. The return from:
a <=> b
Is equivalent to the return from
( a = b OR ( a IS NULL AND b IS NULL ) )
To answer the question you asked, we could write a statement using the NULL-safe comparison <=>
(spaceship) operator, like this:
SELECT `foo`
FROM `bar`
WHERE `a1` <=> :a1
AND `a2` <=> :a2
Or, for a more ANSI standards compliant and portable approach, we could achieve the same result without using that MySQL specific operator, like this:
SELECT `foo`
FROM `bar`
WHERE ( `a1` = :a1 OR ( `a1` IS NULL AND :a1d IS NULL ) )
AND ( `a2` = :a2 OR ( `a2` IS NULL AND :a2d IS NULL ) )
Note that we need to pass in the value of each bind value two times. In the past, PDO has not allowed more than one reference to a bind placeholder. (Not sure if this is still the case in more recent versions of PDO.) The workaround, as demonstrated above, is to use four distinct placeholders in the statement, and supply the same value for :a1
and :a1d
.)
来源:https://stackoverflow.com/questions/42214047/write-a-prepared-statement-with-nullable-values-in-conditions