web-dev-qa-db-fra.com

Obtenir null == null en SQL

Je souhaite rechercher une table de base de données sur une colonne nullable. Parfois, la valeur que je recherche est elle-même NULL. Depuis Null est égal à rien, même NULL, en disant

where MYCOLUMN=SEARCHVALUE 

va échouer. En ce moment je dois recourir à

where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))

Y a-t-il un moyen plus simple de dire cela?

(J'utilise Oracle si ça compte)

44
James Curran

Vous pouvez effectuer les opérations IsNull ou NVL, mais cela ne fera que faire en sorte que le moteur travaille davantage. Vous allez appeler des fonctions pour effectuer des conversions de colonnes qui doivent ensuite comparer les résultats.

Utilisez ce que vous avez

where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
67
Andy Lester

@Andy Lester affirme que la forme originale de la requête est plus efficace que d'utiliser NVL. J'ai décidé de tester cette affirmation:

    SQL> DECLARE
      2    CURSOR B IS
      3       SELECT batch_id, equipment_id
      4         FROM batch;
      5    v_t1  NUMBER;
      6    v_t2  NUMBER;
      7    v_c1  NUMBER;
      8    v_c2  NUMBER;
      9    v_b   INTEGER;
     10  BEGIN
     11  -- Form 1 of the where clause
     12    v_t1 := dbms_utility.get_time;
     13    v_c1 := dbms_utility.get_cpu_time;
     14    FOR R IN B LOOP
     15       SELECT COUNT(*)
     16         INTO v_b
     17         FROM batch
     18        WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL);
     19    END LOOP;
     20    v_t2 := dbms_utility.get_time;
     21    v_c2 := dbms_utility.get_cpu_time;
     22    dbms_output.put_line('For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)');
     23    dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
     24    dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
     25  
     26  -- Form 2 of the where clause
     27    v_t1 := dbms_utility.get_time;
     28    v_c1 := dbms_utility.get_cpu_time;
     29    FOR R IN B LOOP
     30       SELECT COUNT(*)
     31         INTO v_b
     32         FROM batch
     33        WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx');
     34    END LOOP;
     35    v_t2 := dbms_utility.get_time;
     36    v_c2 := dbms_utility.get_cpu_time;
     37    dbms_output.put_line('For clause: WHERE NVL(equipment_id,''xxxx'') = NVL(R.equipment_id,''xxxx'')');
     38    dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
     39    dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
     40  END;
     41  /


    For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)
    CPU seconds used: 84.69
    Elapsed time: 84.8
    For clause: WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx')
    CPU seconds used: 124
    Elapsed time: 124.01

    PL/SQL procedure successfully completed

    SQL> select count(*) from batch;

  COUNT(*)
----------
     20903

SQL> 

J'ai été un peu surpris de découvrir à quel point Andy est correct. La solution NVL coûte presque 50% de plus. Ainsi, même si un élément de code peut ne pas sembler aussi propre et élégant qu'un autre, il peut être considérablement plus efficace. J'ai exécuté cette procédure plusieurs fois et les résultats étaient presque les mêmes à chaque fois. Bravo à Andy ...

36
DCookie

Dans Expert Oracle Database Architecture J'ai vu:

WHERE DECODE(MYCOLUMN, SEARCHVALUE, 1) = 1
13
Peter Meinl

Je ne sais pas si c'est plus simple, mais j'ai parfois utilisé


WHERE ISNULL(MyColumn, -1) = ISNULL(SearchValue, -1)

Remplacer "-1" par une valeur valide pour le type de colonne, mais également non susceptible d'être trouvée dans les données.

NOTE: J'utilise MS SQL, pas Oracle, donc je ne sais pas si "ISNULL" est valide.

12
Chris Shaffer

Utilisez NVL pour remplacer null par une valeur fictive des deux côtés, comme dans:

WHERE NVL(MYCOLUMN,0) = NVL(SEARCHVALUE,0)
7
JosephStyons

Une autre alternative, probablement optimale du point de vue de la requête exécutée, et ne sera utile que si vous effectuez un type de génération de requête est de générer la requête exacte dont vous avez besoin en fonction de la valeur de recherche.

Le pseudocode suit.

if (SEARCHVALUE IS NULL) {
    condition = 'MYCOLUMN IS NULL'
} else {
    condition = 'MYCOLUMN=SEARCHVALUE'
}
runQuery(query,condition)
5
Vinko Vrsalovic

Si une valeur hors bande est possible:

where coalesce(mycolumn, 'out-of-band') 
    = coalesce(searchvalue, 'out-of-band')
2
Ted

Cela peut également faire le travail dans Oracle.

WHERE MYCOLUMN || 'X'  = SEARCHVALUE || 'X'

Dans certaines situations, il bat le test IS NULL avec le OU.

J'ai également été surpris que DECODE vous permette de comparer NULL à NULL.

WITH 
TEST AS
(
    SELECT NULL A FROM DUAL
)
SELECT DECODE (A, NULL, 'NULL IS EQUAL', 'NULL IS NOT EQUAL')
FROM TEST
1
EvilTeach

Essayer 

WHERE NVL(mycolumn,'NULL') = NVL(searchvalue,'NULL')
1
DCookie

C'est une situation dans laquelle nous nous trouvons souvent avec nos fonctions Oracle qui génèrent des rapports. Nous voulons permettre aux utilisateurs d'entrer une valeur pour limiter les résultats ou la laisser vide pour renvoyer tous les enregistrements. C'est ce que j'ai utilisé et cela a bien fonctionné pour nous.

WHERE rte_pending.ltr_rte_id = prte_id
  OR ((rte_pending.ltr_rte_id IS NULL OR rte_pending.ltr_rte_id IS NOT NULL)
      AND prte_id IS NULL)
0
Jason Winger

Je penserais que ce que tu as est OK. Vous pourriez peut-être utiliser:

where NVL(MYCOLUMN, '') = NVL(SEARCHVALUE, '')
0
Carl