2011-09-02 17 views
18

1) .Hello, tengo los siguientes códigos de Oracle PL/SQL que puede ser oxidado de ustedes perspectiva:Oracle PL cadena/SQL comparar tema

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

Esto es muy obvio que dos cadenas cadena1 y cadena2 no son iguales, pero ¿por qué 'Las dos cuerdas no son iguales' no se imprimió? ¿Oracle tiene otro método común para comparar dos cadenas?

+7

Tenga en cuenta que en Oracle una cadena vacía es equivalente a 'NULL' que puede no funcionar como se esperaba en una declaración de comparación de desigualdad – Phil

+0

¿podría darme algunos consejos sobre cómo resolver este problema? –

+2

Estas son dos preguntas distintas. Deberías preguntarles como tal, ya que así es como funciona SO. Una vez dicho esto, tu segunda pregunta es una castaña vieja que ya ha sido respondida varias veces. Por favor, busque antes de preguntar: http://stackoverflow.com/questions/tagged/oracle+string-aggregation – APC

Respuesta

40

Como notó Phil, la cadena vacía se trata como NULL, y NULL no es igual o desigual a nada. Si espera que las cadenas vacías o NULL, que necesita para manejar aquellos con 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; 
/

En cuanto a las comparaciones nulos:

De acuerdo con la Oracle 12c documentation on NULLS, las comparaciones nulos utilizando IS NULL o hacer IS NOT NULL evaluar a TRUE o FALSE. Sin embargo, todas las demás comparaciones se evalúan a UNKNOWN, y no aFALSE. La documentación además establece:

Una condición que evalúa a actos DESCONOCIDOS casi como FALSO. Por ejemplo, una instrucción SELECT con una condición en la cláusula WHERE que evalúa a UNKNOWN no ​​devuelve filas. Sin embargo, una condición que evalúa DESCONOCIDO difiere de FALSO en que las operaciones posteriores en una evaluación de condición DESCONOCIDA se evaluarán como DESCONOCIDAS. Por lo tanto, NO FALSO se evalúa como VERDADERO, pero NO DESCONOCIDO se evalúa como DESCONOCIDO.

una tabla de referencia es proporcionada por 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 

También aprendí que no hay que escribir PL/SQL asumiendo cadenas vacías siempre evaluar como NULL: Base de datos

Oracle actualmente trata un valor de carácter con una longitud de cero como nulo. Sin embargo, esto puede no continuar siendo cierto en versiones futuras, y Oracle recomienda que no trate las cadenas vacías lo mismo que las nulas.

+5

Mientras prácticamente correcto, la definición precisa es que "cualquier comparación que implique un valor NULL siempre devolverá FALSE" (incluso si se compara con otro valor NULL). Entonces: (NULL = NULL) = False AND (NULL <> NULL) = FALSO – Kerbocat

+1

@Kerbocat He estado reflexionando sobre este comentario durante los últimos años. ¿Tiene alguna referencia sobre cómo Oracle evalúa las comparaciones nulas? The Oracle [documentation] (http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements005.htm#g194888) Encuentro estados que las comparaciones nulas realmente evalúan como 'UNKNOWN '. – Wolf

+0

No tengo referencias para esto ahora, bien podría ser que de hecho estoy equivocado.Sin embargo, considero que UNKNOWN es de muy poco uso práctico como concepto dado esto: (de su fuente) "Una condición que evalúa a actos DESCONOCIDOS casi como FALSA. Por ejemplo, una declaración SELECT con una condición en la cláusula WHERE que evalúa DESCONOCIDO no devuelve filas. Sin embargo, una condición que evalúa DESCONOCIDO difiere de FALSO en que las operaciones posteriores en una evaluación de condición DESCONOCIDA se evaluarán como DESCONOCIDO. Por lo tanto, NO FALSO se evalúa como VERDADERO, pero NO DESCONOCIDO se evalúa como DESCONOCIDO. " – Kerbocat

-5

A la primera pregunta:

Probablemente, el mensaje no se imprime porque tiene la salida desactivada. Utilice estos comandos para volver a encenderla:

set serveroutput on 
exec dbms_output.enable(1000000); 

Sobre la segunda pregunta:

Mi PLSQL es bastante oxidado, así que no puedo dar un fragmento completo, pero necesitará para recorrer la conjunto de resultados de la consulta SQL y CONCAT todas las cadenas juntas.

+0

He establecido en realidad la salida del servidor, pero todavía no se puede mostrar –

2

Comparo cadenas usando = y no <>. Descubrí que en este contexto = parece funcionar de manera más razonable que <>. He especificado que dos cadenas vacías (o NULAS) son iguales. La implementación real devuelve booleano PL/SQL, pero aquí lo cambié a pls_integer (0 es falso y 1 es verdadero) para poder demostrar fácilmente la función.

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; 
/
11

Vamos a llenar los vacíos en su código, mediante la adición de las otras ramas de la lógica, y ver lo que sucede:

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> 

Así que las dos cadenas no son ni el mismo ni no son ellos ¿mismo? ¿Huh?

Se trata de esto. Oracle trata una cadena vacía como NULL. Si intentamos comparar un NULO y otra cadena, el resultado no es VERDADERO ni FALSO, es NULO. Este sigue siendo el caso incluso si la otra cadena también es NULL.

1

Para solucionar la pregunta principal, "¿cómo debo detectar que estas dos variables no tienen el mismo valor cuando una de ellas es nula?", No me gusta el enfoque de nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that') porque no siempre se puede garantizar que no aparecerá un valor ... especialmente con NUMBER.

He utilizado el siguiente:

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

Negación: No soy un asistente de Oracle y se me ocurrió esto a mí mismo y no lo he visto en otros lugares, lo que puede haber alguna razón sutil por qué es una mala idea. Pero evita la trampa mencionada por APC, que al comparar un valor nulo con algo diferente no da VERDADERO ni FALSO, sino NULO. Porque las cláusulas (str1 is null) siempre devolverán VERDADERO o FALSO, nunca nulo.

(Tenga en cuenta que PL/SQL realiza la evaluación de cortocircuito, como se ha señalado here.)

0

He creado una función almacenada para este propósito comparación de texto:

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; 

En el ejemplo:

SELECT * FROM MyTable WHERE TextCompare(MyTable.a, '>=', MyTable.b) = 1; 
Cuestiones relacionadas