Write a prepared statement with nullable values in conditions

浪子不回头ぞ 提交于 2020-11-24 18:02:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!