Oracle PL / SQL string question de comparaison

1).Bonjour, j'ai les codes Oracle PL/SQL suivants qui peuvent être rouillés de votre point de vue:

 DECLARE
 str1  varchar2(4000);
 str2  varchar2(4000);
 BEGIN
   str1:='';
   str2:='sdd';
   IF(str1<>str2) THEN
    dbms_output.put_line('The two strings is not equal');
   END IF;
 END;
 /

c'est très évident que deux chaînes str1 et str2 ne sont pas égales, mais pourquoi "les deux chaînes ne sont pas égales" n'a pas été imprimé? Oracle a-t-il une autre méthode commune pour comparer deux chaînes?

20
demandé sur C.c 2011-09-02 19:52:57

6 réponses

comme Phil l'a noté, la chaîne vide est traitée comme nulle, et nulle N'est égale ou inégale à quoi que ce soit. Si vous attendez des chaînes vides ou NULLs, vous aurez besoin de gérer ceux avec NVL() :

 DECLARE
 str1  varchar2(4000);
 str2  varchar2(4000);
 BEGIN
   str1:='';
   str2:='sdd';
-- Provide an alternate null value that does not exist in your data:
   IF(NVL(str1,'X') != NVL(str2,'Y')) THEN
    dbms_output.put_line('The two strings are not equal');
   END IF;
 END;
 /

concernant les comparaisons nulles:

selon la documentation Oracle 12c sur NULLS , comparaisons nulles en utilisant IS NULL ou IS NOT NULL do évaluer à TRUE ou FALSE . Cependant, toutes les autres comparaisons évaluent à UNKNOWN , et non FALSE . La documentation ajoute:

une condition qui évalue à des actes inconnus presque comme faux. Par exemple, une instruction SELECT avec une condition dans la clause WHERE qui évalue à un inconnu ne renvoie aucune ligne. Toutefois, une condition évaluant à inconnu diffère de FALSE en ce que d'autres opérations sur un L'évaluation de l'état Inconnu sera évaluée à L'inconnu. Ainsi, pas faux évalue à vrai, mais pas inconnu évalue à inconnu.

un tableau de référence est fourni par Oracle:

Condition       Value of A    Evaluation
----------------------------------------
a IS NULL       10            FALSE
a IS NOT NULL   10            TRUE        
a IS NULL       NULL          TRUE
a IS NOT NULL   NULL          FALSE
a = NULL        10            UNKNOWN
a != NULL       10            UNKNOWN
a = NULL        NULL          UNKNOWN
a != NULL       NULL          UNKNOWN
a = 10          NULL          UNKNOWN
a != 10         NULL          UNKNOWN

j'ai aussi appris que nous ne devrions pas écrire PL / SQL en supposant que les chaînes vides seront toujours évaluées comme NULL:

Oracle Database traite actuellement une valeur de caractère avec une longueur de zéro comme nulle. Cependant, ce peut ne pas continuer à être vrai dans les versions futures, et Oracle recommande que vous ne traitiez pas les chaînes vides de la même manière que nulls.

42
répondu Wolf 2016-05-03 20:33:28

remplissons les lacunes de votre code, en ajoutant les autres branches dans la logique, et voyons ce qui se passe:

SQL> DECLARE
  2   str1  varchar2(4000);
  3   str2  varchar2(4000);
  4  BEGIN
  5     str1:='';
  6     str2:='sdd';
  7     IF(str1<>str2) THEN
  8      dbms_output.put_line('The two strings is not equal');
  9     ELSIF (str1=str2) THEN
 10      dbms_output.put_line('The two strings are the same');
 11     ELSE
 12      dbms_output.put_line('Who knows?');
 13     END IF;
 14   END;
 15  /
Who knows?

PL/SQL procedure successfully completed.

SQL>

donc les deux cordes ne sont ni les mêmes ni les mêmes? Hein?

ça se résume à ça. Oracle traite une chaîne vide comme nulle. Si nous essayons de comparer un NULL et une autre chaîne, le résultat n'est ni vrai ni faux, il est NULL. Ceci reste le cas même si l'autre chaîne est également nulle.

11
répondu APC 2011-09-05 15:12:53

je compare les cordes en utilisant = et non <> . J'ai découvert que dans ce contexte = semble fonctionner de façon plus raisonnable que <> . J'ai spécifié que deux chaînes vides (ou NULL) sont égales. L'implémentation réelle renvoie le booléen PL/SQL, mais ici j'ai changé cela en pls_integer (0 est false et 1 est true) pour pouvoir facilement démontrer la fonction.

create or replace function is_equal(a in varchar2, b in varchar2)
return pls_integer as
begin
  if a is null and b is null then
    return 1;
  end if;

  if a = b then
    return 1;
  end if;

  return 0;
end;
/
show errors

begin
  /* Prints 0 */
  dbms_output.put_line(is_equal('AAA', 'BBB'));
  dbms_output.put_line(is_equal('AAA', null));
  dbms_output.put_line(is_equal(null, 'BBB'));
  dbms_output.put_line(is_equal('AAA', ''));
  dbms_output.put_line(is_equal('', 'BBB'));

  /* Prints 1 */
  dbms_output.put_line(is_equal(null, null));
  dbms_output.put_line(is_equal(null, ''));
  dbms_output.put_line(is_equal('', ''));
  dbms_output.put_line(is_equal('AAA', 'AAA'));
end;
/
2
répondu user272735 2011-09-04 12:03:02

pour corriger la question centrale, " Comment puis-je détecter que ces deux variables n'ont pas la même valeur quand l'une d'elles est nulle?", Je n'aime pas l'approche de nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that') parce que vous ne pouvez pas toujours garantir qu'une valeur n'apparaîtra pas... surtout avec les chiffres.

j'ai utilisé ce qui suit:

if (str1 is null) <> (str2 is null) or str1 <> str2 then
  dbms_output.put_line('not equal');
end if;

avertissement: Je ne suis pas un magicien Oracle et je suis venu avec celui-ci moi-même et ne l'ai pas vu ailleurs, de sorte qu'il peut y avoir certains subtil raison pourquoi c'est une mauvaise idée. Mais il évite le piège mentionné par APC, qui compare un nul à quelque chose d'autre ne donne ni vrai ni faux mais nul. Parce que les clauses (str1 is null) retourneront toujours vrai ou faux, jamais null.

(notez que PL/SQL effectue une évaluation de court-circuit, comme indiqué ici .)

1
répondu Andrew Spencer 2017-05-23 12:17:56

j'ai créé une fonction stockée pour cette comparaison de texte but:

CREATE OR REPLACE FUNCTION TextCompare(vOperand1 IN VARCHAR2, vOperator IN VARCHAR2, vOperand2 IN VARCHAR2) RETURN NUMBER DETERMINISTIC AS
BEGIN
  IF vOperator = '=' THEN
    RETURN CASE WHEN vOperand1 = vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = '<>' THEN
    RETURN CASE WHEN vOperand1 <> vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END;
  ELSIF vOperator = '<=' THEN
    RETURN CASE WHEN vOperand1 <= vOperand2 OR vOperand1 IS NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = '>=' THEN
    RETURN CASE WHEN vOperand1 >= vOperand2 OR vOperand2 IS NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = '<' THEN
    RETURN CASE WHEN vOperand1 < vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NOT NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = '>' THEN
    RETURN CASE WHEN vOperand1 > vOperand2 OR vOperand1 IS NOT NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = 'LIKE' THEN
    RETURN CASE WHEN vOperand1 LIKE vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
  ELSIF vOperator = 'NOT LIKE' THEN
    RETURN CASE WHEN vOperand1 NOT LIKE vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END;
  ELSE
    RAISE VALUE_ERROR;
  END IF;
END;

dans l'exemple:

SELECT * FROM MyTable WHERE TextCompare(MyTable.a, '>=', MyTable.b) = 1;
0
répondu David Gausmann 2017-08-10 07:39:33

à la première question:

probablement le message n'a pas été imprimé parce que vous avez désactivé la sortie. Utilisez ces commandes pour le rallumer:

set serveroutput on
exec dbms_output.enable(1000000);

sur la deuxième question:

mon PLSQL est assez rouillé donc je ne peux pas vous donner un extrait complet, mais vous aurez besoin de boucler sur le jeu de résultats de la requête SQL et CONCATER toutes les chaînes ensemble.

-5
répondu João Rafael 2011-09-02 16:03:21