2017-02-13 3 views
4

Y at-il un moyen d'écrire une instruction préparée où une valeur est comparée à une autre valeur dans une condition et je ne sais pas, si cette valeur est NULL ou non.Écrire une instruction préparée avec des valeurs Null dans des conditions

SELECT `foo` FROM `bar` WHERE `a1` = :a1 AND `a2` = :a2 

Si j'utiliser cette déclaration préparée avec a1 => null et a2 => 42, la requête résultante serait:

SELECT `foo` FROM `bar` WHERE `a1` = NULL AND `a2` = '42' 

Ceci est, bien sûr, pas ce que je veux. Je besoin de cela dans ce cas:

SELECT `foo` FROM `bar` WHERE `a1` IS NULL AND `a2` = '42' 
            ^^ 

Les deux a1 et a2 sont annulable. Je ne veux pas définir 4 préparer des déclarations:

-- 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 

Répondre

4

MySQL fournit une comparaison sécurité nulle <=> opérateur (vaisseau spatial). Cela spécifie une comparaison d'égalité qui retournera TRUE ou FALSE, et ne retournera pas NULL lorsque l'un des opérandes est NULL.

En démonstration:

SELECT NULL=NULL 
    , NULL<=>NULL 
    , 1=NULL 
    , 1<=>NULL 
    , 1=0 
    , 1<=>0 
    , 1=1 
    , 1<=>1 

Retours:

NULL=NULL NULL<=>NULL 1=NULL 1<=>NULL  1=0 1<=>0  1=1 1<=>1 
--------- ----------- ------ -------- ------ ----- ------ ----- 
    (NULL)   1 (NULL)   0  0  0  1  1 

Cette opération de comparaison est essentiellement un raccourci. Le retour de:

a <=> b 

est équivalent au retour de

(a = b OR (a IS NULL AND b IS NULL)) 

Pour répondre à la question que vous avez posée, nous pourrions écrire une déclaration en utilisant la comparaison NULL-safe <=> (vaisseau spatial) opérateur, comme ceci:

SELECT `foo` 
    FROM `bar` 
    WHERE `a1` <=> :a1 
    AND `a2` <=> :a2 

Ou, pour une approche compatible et portable plus des normes ANSI, nous pourrions obtenir le même résultat sans utiliser cet opérateur spécifique MySQL, comme ceci:

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)) 

Notez que nous devons passer la valeur de chaque bind valeur deux fois. Dans le passé, PDO n'a pas autorisé plus d'une référence à un espace réservé bind. (Je ne sais pas si cela est encore le cas dans les versions les plus récentes de PDO.) La solution, comme l'a démontré ci-dessus, est d'utiliser quatre placeholders distincts dans la déclaration, et de fournir la même valeur pour :a1 et :a1d.)

+0

Je vous remercie! Je ne savais pas cet opérateur (et aurait pu figurer la solution avec le 'OU' moi-même ..!) – stofl

+1

@stofl: Je suis content que vous ayez trouvé cela utile. Pour mettre en évidence quelque chose que je n'ai peut-être pas suffisamment souligné ... que ** '<=>' ** L'opérateur de comparaison est une extension * non standard * disponible * seulement * dans MySQL. (Au moins, je ne connais aucun autre SGBD qui supporte cet opérateur. – spencer7593

+0

Merci d'avoir mentionné cela. Ce n'est pas pertinent dans mon cas, car nous ne restons pas indépendants de toute façon. – stofl