Проблема сравнения строк в Oracle PL / SQL - PullRequest
21 голосов
/ 02 сентября 2011

1). Здравствуйте, у меня есть следующие коды Oracle PL / SQL, которые могут быть ржавыми с вашей точки зрения, ребята:

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

Это очень очевидно, что две строки str1 и str2 не равны, но почему «Две строки не равны» не было распечатано? Есть ли у Oracle другой общий метод для сравнения двух строк?

Ответы [ 7 ]

46 голосов
/ 02 сентября 2011

Как заметил Фил, пустая строка обрабатывается как NULL, а NULL не равно и не равно чему-либо.Если вы ожидаете пустые строки или значения NULL, вам нужно обработать их с 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;
 /

Относительно нулевых сравнений:

Согласно Документация Oracle 12c по NULLS , нулевые сравнения с использованием IS NULL или IS NOT NULL оценивают как TRUE или FALSE.Однако все другие сравнения оцениваются как UNKNOWN, , а не FALSE.Документация также гласит:

Условие, которое оценивается как НЕИЗВЕСТНОЕ, действует почти как ЛОЖЬ.Например, инструкция SELECT с условием в предложении WHERE, которое оценивается как UNKNOWN, не возвращает строк.Однако условие оценки UNKNOWN отличается от FALSE тем, что дальнейшие операции оценки состояния UNKNOWN будут оцениваться как UNKNOWN.Таким образом, NOT FALSE оценивается как TRUE, но NOT UNKNOWN оценивается как UNKNOWN.

Справочная таблица предоставляется 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

Я также узнал, что мы не должны писать PL/ SQL при условии, что пустые строки всегда будут оцениваться как NULL:

База данных Oracle в настоящее время обрабатывает символьное значение с нулевой длиной как ноль.Однако в будущих выпусках это может не соответствовать действительности, и Oracle рекомендует не рассматривать пустые строки так же, как пустые.

11 голосов
/ 05 сентября 2011

Давайте заполним пробелы в вашем коде, добавив другие ветви в логику, и посмотрим, что произойдет:

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>

То есть две строки не одинаковы и не одинаковы?А?

Все сводится к этому.Oracle обрабатывает пустую строку как NULL.Если мы попытаемся сравнить NULL и другую строку, результат не TRUE или FALSE, это NULL.Это остается в силе, даже если другая строка также равна NULL.

2 голосов
/ 04 сентября 2011

Я сравниваю строки, используя =, а не <>.Я обнаружил, что в этом контексте =, кажется, работает более разумно, чем <>.Я указал, что две пустые (или NULL) строки равны.Реальная реализация возвращает PL / SQL логическое значение, но здесь я изменил его на pls_integer (0 - false, а 1 - true), чтобы можно было легко продемонстрировать функцию.

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;
/
1 голос
/ 29 января 2016

Чтобы исправить основной вопрос: «Как мне обнаружить, что эти две переменные не имеют одинаковое значение, когда одна из них равна нулю?», Мне не нравится подход nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that'), потому что вы не можетевсегда гарантируйте, что значение не появится ... особенно с NUMBER.

Я использовал следующее:

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

Отказ от ответственности: я не являюсь мастером Oracle, и я придумалэтот сам и не видел его в другом месте, так что может быть какая-то тонкая причина, почему это плохая идея.Но он избегает ловушки, упомянутой APC, что сравнение нуля с чем-то другим не дает ни ИСТИНЫ, ни ЛОЖИ, а НУЛЯ.Поскольку пункты (str1 is null) всегда будут возвращать ИСТИНА или ЛОЖЬ, а не нуль.

(Обратите внимание, что PL / SQL выполняет оценку короткого замыкания, как отмечено здесь .)

0 голосов
/ 08 ноября 2018

только изменить строку str1: = ''; в str1: = '';

0 голосов
/ 10 августа 2017

Я создал хранимую функцию для сравнения текста:

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;

Например:

SELECT * FROM MyTable WHERE TextCompare(MyTable.a, '>=', MyTable.b) = 1;
0 голосов
/ 02 сентября 2011

К первому вопросу:

Возможно, сообщение не было распечатано, потому что у вас отключен выход.Используйте эти команды, чтобы включить его:

set serveroutput on
exec dbms_output.enable(1000000);

По второму вопросу:

Мой PLSQL довольно ржавый, поэтому я не могу дать вам полный фрагмент, но вам понадобитсязациклить на наборе результатов запроса SQL и CONCAT все строки вместе.

...