Сбой функции TO_NUMBER в postgreSQL с дополнительными ограничениями WHERE? - PullRequest
0 голосов
/ 01 мая 2018

У меня есть таблица в postgres, содержащая разобранную информацию об адресе. Когда я запускаю следующий запрос, я получаю правильные результаты:

select count(*) from address_table
  where (mod(to_number(to_address_left, '99999999'), 2) = 0 
         and to_address_left <> ' ')

Но когда я добавляю к нему ИЛИ ...

select count(*) from address_table
  where (mod(to_number(to_address_left, '99999999'), 2) = 0 
         and to_address_left <> ' ')
     or (from_address_left <> ' ')

Я получаю следующую ошибку ...

ERROR:  invalid input syntax for type numeric: " "
SQL state: 22P02

Я считаю, что это ошибка, связанная с функцией TO_NUMBER. Я знаю, что в некоторых из этих записей есть пробелы (преднамеренно), поэтому "and to_address_left <> ''" включен. Но я не понимаю, почему добавление оператора OR, который ссылается на совершенно другое поле, приводит к тому, что в противном случае автономная функция TO_NUMBER генерирует ошибку.

Этот код отлично работает в Oracle SQL. Почему здесь не работает?

1 Ответ

0 голосов
/ 01 мая 2018

SQL является декларативным языком. Планировщик запросов может свободно оценивать выражения в любом понравившемся порядке, если только он дает правильный результат. Таким образом, если вы добавите предложение OR, оно может сгенерировать план запроса, который сначала оценивает to_number, а затем to_address_left <> ' '. Это вызовет исключение для строк с пробелом в to_address_left.

Последовательное решение этой проблемы заключается в том, чтобы оценка to_number никогда не вызывала исключений. Похоже, to_number успешно выполняется для любой строки, которая содержит число, даже 'b 1 d', так что вы можете проверить это в case:

to_number(case when str_col ~ '[0-9]' then str_col end, '99999999')

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

Альтернативный способ, при котором Postgres материализует выражение общей таблицы (что пока делают все версии Postgres, но его нет в стандарте SQL):

with    CTE as
        (
        select  *
        from    YourTable
        where   str_col not like '% %'
        )
select  *
from    CTE
-- Line below below guaranteed not to run for rows with a space in str_col
where   to_number(str_col) > 42
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...