Почему функция ORACLE TO_DATE не выдает ошибку ORA-01841 в условии ИЛИ при передаче пробелов?

SQL> select to_date('    ','YYYYMMDD') from dual;
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, not be 0

SQL> select * from dual where to_date('    ','YYYYMMDD') = '19960512' or 1 = 2;
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, not be 0

SQL> select * from dual where to_date('    ','YYYYMMDD') = '19960512' or 1 = 1;
Success.

В первых двух операторах мы передаем пробелы в функцию TO_DATE, которая выдает ожидаемую ошибку. Но

Теперь в третьем операторе мы передаем пробелы в функцию оракула TO_DATE, что не разрешено. Но все же он выполняется успешно.

Почему?

В реальном коде я могу передать пробелы из переменной, а часть ИЛИ может быть или не быть правдой.

Кто-нибудь может объяснить мне это поведение и как справиться с ошибкой?

P.S. Я использую Oracle 12g EE


person Shubham Khandare    schedule 19.12.2018    source источник


Ответы (3)


Oracle использует здесь логику короткого замыкания. Поскольку известно, что 1=1 истинно, Oracle оптимизирует оценку другого условия. Поскольку он никогда не оценивается, тот факт, что вы передаете ему недопустимые аргументы, не имеет значения.

person Mureinik    schedule 19.12.2018
comment
Проголосовали за, но было бы неплохо увидеть ссылку на документ для вашего ответа, потому что AFAIK в SQL, как правило, нет гарантированного порядка короткого замыкания (в отличие от языков приложений, таких как Java и C#, которые делают имеют такие гарантированные правила). - person Tim Biegeleisen; 19.12.2018
comment
Джастин Кэйв здесь / утверждает, что оптимизатор ищет истину в любом порядке, чтобы он мог сократить набор предикатов, но без ссылки на документы оракула - person Caius Jard; 19.12.2018
comment
Я добавил 1=1 ради условия ИЛИ, но эта часть зависит от потока, который может быть или не быть правдой. Но как я могу убедиться, что получаю сообщение об ошибке, когда пробелы передаются в TO_DATE? - person Shubham Khandare; 19.12.2018
comment
добавление 1=1 повлияло на результат. Если добавить дополнительное условие, подобное этому: select * from dual where to_date(' ','YYYYMMDD') = '19960512' or to_date('19960512','YYYYMMDD') = '19960512', вы получите ожидаемую ошибку. Таким образом, решение: как обработать ошибку? не использовать 1=1 - person Paul Maxwell; 19.12.2018

Другой ответ не касался той части вашего вопроса, где вы все еще хотели получить ошибку.

Я рекомендую вам выполнять синтаксический анализ даты изолированно. Если это хранимая процедура, не помещайте запрос, как вы сделали здесь, создайте переменную даты, используйте to_date, чтобы присвоить ей значение даты (это произойдет сбой, если пользователь укажет пробелы), затем используйте переменную даты в запросе. Таким образом, если у оракула будут предоставлены неверные данные, у них все еще есть шанс подавиться ими.

В качестве альтернативы сделайте так, чтобы ваша процедура хранения принимала параметр типа даты, а не строку, чтобы заставить вызывающую программу предоставить что-то разумное и последовательное дату

person Caius Jard    schedule 19.12.2018
comment
Эти запросы с условием или находятся на уровне данных, а строка из базы данных извлекается и передается в TO_DATE в запросе. - person Shubham Khandare; 19.12.2018
comment
Я как бы не совсем понял, что вы имели в виду, но если вы чувствуете, что нет другого пути, кроме как задать такой запрос, вам придется сделать что-то еще, чтобы вызвать сбой; выберите to_date, а также сделайте это в предложении where или поместите его в случае, когда это приводит к порядку. Я все еще думаю, что если вы говорите, что запрос - это все, что есть, вы должны правильно его параметризовать. В этой дате не должно быть пробелов. Есть еще один аргумент: если вы ожидаете неверных данных определенного типа, вы должны обработать их, а не сбой, чтобы свести к минимуму объем работы, которую должна выполнить команда технической поддержки. - person Caius Jard; 19.12.2018

Вы можете повторить преобразование в ветке or, что, очевидно, немного запутано:

select * from dual
where to_date('    ','YYYYMMDD') = '19960512'
or (1 = 1 and to_date('    ','YYYYMMDD') != '19960512');

Error report -
ORA-01841: (full) year must be between -4713 and +9999, and not be 0

Или, если единственным сценарием недопустимой даты, о котором вам нужно беспокоиться, являются пробелы, вы можете сделать что-то еще, чтобы в этом случае эта ветвь оценивалась как ложная:

select * from dual
where to_date('    ','YYYYMMDD') = '19960512'
or (1 = 1 and trim('    ') is not null);

строка из базы данных извлекается и передается в TO_DATE в запросе

подразумевает, что вы храните даты в виде строк в своей базе данных, что не очень хорошая идея; поэтому, если у вас могут быть пробелы, у вас может быть что угодно, и вам также придется иметь дело с другими потенциальными ошибками, которые подход is not null не поймает.

И ни один из этих запросов не приведет к ошибке с переданной пустой строкой (т.е. null), но тогда и ваши первые два исходных запроса не будут; все они просто не будут найдены.


Конечно, '19960512' — это не дата, поэтому здесь вы выполняете дополнительные неявные преобразования; было бы безопаснее также передать это через to_date() или использовать литерал даты, если это действительно фиксированное значение:

where to_date('    ','YYYYMMDD') = date '1996-05-12'
person Alex Poole    schedule 19.12.2018