Как преобразовать строку в целое число и получить 0 в случае ошибки при преобразовании с PostgreSQL? - PullRequest
108 голосов
/ 18 января 2010

В PostgreSQL у меня есть таблица со столбцом varchar. Предполагается, что данные должны быть целыми числами, и в запросе они нужны мне как целочисленные. Некоторые значения являются пустыми строками. Следующее:

SELECT myfield::integer FROM mytable

урожайность ERROR: invalid input syntax for integer: ""

Как я могу запросить приведение и иметь 0 в случае ошибки во время приведения в postgres?

Ответы [ 11 ]

141 голосов
/ 24 мая 2010

Я просто боролся с подобной проблемой сам, но не хотел накладных расходов на функцию. Я придумал следующий запрос:

SELECT myfield::integer FROM mytable WHERE myfield ~ E'^\\d+$';

Postgres сокращает его условные обозначения, поэтому вы не должны получать никаких нецелых чисел, попадающих в ваш :: integer приведение. Он также обрабатывает значения NULL (они не будут соответствовать регулярному выражению).

Если вы хотите, чтобы нули вместо того, чтобы не выбирать, то оператор CASE должен работать:

SELECT CASE WHEN myfield~E'^\\d+$' THEN myfield::integer ELSE 0 END FROM mytable;
87 голосов
/ 19 января 2010

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

CREATE OR REPLACE FUNCTION convert_to_integer(v_input text)
RETURNS INTEGER AS $$
DECLARE v_int_value INTEGER DEFAULT NULL;
BEGIN
    BEGIN
        v_int_value := v_input::INTEGER;
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Invalid integer value: "%".  Returning NULL.', v_input;
        RETURN NULL;
    END;
RETURN v_int_value;
END;
$$ LANGUAGE plpgsql;

Тестирование:

=# select convert_to_integer('1234');
 convert_to_integer 
--------------------
               1234
(1 row)

=# select convert_to_integer('');
NOTICE:  Invalid integer value: "".  Returning NULL.
 convert_to_integer 
--------------------

(1 row)

=# select convert_to_integer('chicken');
NOTICE:  Invalid integer value: "chicken".  Returning NULL.
 convert_to_integer 
--------------------

(1 row)
24 голосов
/ 02 августа 2013

У меня была такая же потребность, и я обнаружил, что это хорошо работает для меня (postgres 8.4):

CAST((COALESCE(myfield,'0')) AS INTEGER)

Некоторые тестовые примеры для демонстрации:

db=> select CAST((COALESCE(NULL,'0')) AS INTEGER);
 int4
------
    0
(1 row)

db=> select CAST((COALESCE('','0')) AS INTEGER);
 int4
------
    0
(1 row)

db=> select CAST((COALESCE('4','0')) AS INTEGER);
 int4
------
    4
(1 row)

db=> select CAST((COALESCE('bad','0')) AS INTEGER);
ERROR:  invalid input syntax for integer: "bad"

Если вам нужно обработать возможность того, что поле содержит нечисловой текст (например, «100bad»), вы можете использовать regexp_replace для удаления нечисловых символов перед приведением.

CAST(REGEXP_REPLACE(COALESCE(myfield,'0'), '[^0-9]+', '', 'g') AS INTEGER)

Тогда значения типа text / varchar, такие как "b3ad5", также будут давать числа

db=> select CAST(REGEXP_REPLACE(COALESCE('b3ad5','0'), '[^0-9]+', '', 'g') AS INTEGER);
 regexp_replace
----------------
             35
(1 row)

Чтобы ответить на вопрос Криса Когдона о решении, не дающем 0 для всех случаев, включая случай «плохой» (без цифр), я сделал это скорректированное утверждение:

CAST((COALESCE(NULLIF(REGEXP_REPLACE(myfield, '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);

Он работает аналогично более простым решениям, за исключением того, что даст 0, когда значение для преобразования состоит только из нецифровых символов, например, «плохо»:

db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
     coalesce
----------
        0
(1 row)
19 голосов
/ 19 апреля 2013

Это может быть чем-то вроде взлома, но в нашем случае это выполнено:

(0 || myfield)::integer

Пояснение (протестировано на Postgres 8.4):

Вышеупомянутое выражение дает NULL для NULL-значений в myfield и 0 для пустых строк (Такое точное поведение может соответствовать или не соответствовать вашему варианту использования).

SELECT id, (0 || values)::integer from test_table ORDER BY id

Данные испытаний:

CREATE TABLE test_table
(
  id integer NOT NULL,
  description character varying,
  "values" character varying,
  CONSTRAINT id PRIMARY KEY (id)
)

-- Insert Test Data
INSERT INTO test_table VALUES (1, 'null', NULL);
INSERT INTO test_table VALUES (2, 'empty string', '');
INSERT INTO test_table VALUES (3, 'one', '1');

Запрос даст следующий результат:

 ---------------------
 |1|null        |NULL|
 |2|empty string|0   |
 |3|one         |1   |
 ---------------------

В то время как выбор только values::integer приведет к сообщению об ошибке.

Надеюсь, это поможет.

3 голосов
/ 23 июля 2016

@ Ответ Мэтью хорошо. Но это может быть проще и быстрее. И вопрос состоит в том, чтобы преобразовать пустые строки ('') в 0, но не в другой «неверный синтаксис ввода» или «вне диапазона» ввода:

CREATE OR REPLACE FUNCTION convert_to_int(text)
  RETURNS int AS
$func$
BEGIN
   IF $1 = '' THEN  -- special case for empty string like requested
      RETURN 0;
   ELSE
      RETURN $1::int;
   END IF;

EXCEPTION WHEN OTHERS THEN
   RETURN NULL;  -- NULL for other invalid input

END
$func$  LANGUAGE plpgsql IMMUTABLE;

Возвращает 0 для пустой строки и NULL для любого другого неверного ввода.
Его можно легко адаптировать для любого преобразования типа данных .

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

3 голосов
/ 18 января 2010

SELECT CASE WHEN myfield="" THEN 0 ELSE myfield::integer END FROM mytable

Я никогда не работал с PostgreSQL, но я проверил в руководстве правильный синтаксис операторов IF в запросах SELECT.

1 голос
/ 02 июля 2016

Я нашел следующий код простым и работающим. Оригинальный ответ здесь https://www.postgresql.org/message-id/371F1510.F86C876B@sferacarta.com

prova=> create table test(t text, i integer);
CREATE

prova=> insert into test values('123',123);
INSERT 64579 1

prova=> select cast(i as text),cast(t as int)from test;
text|int4
----+----
123| 123
(1 row)

надеюсь, это поможет

1 голос
/ 26 апреля 2016
CREATE OR REPLACE FUNCTION parse_int(s TEXT) RETURNS INT AS $$
BEGIN
  RETURN regexp_replace(('0' || s), '[^\d]', '', 'g')::INT;
END;
$$ LANGUAGE plpgsql;

Эта функция всегда возвращает 0, если во входной строке нет цифр.

SELECT parse_int('test12_3test');

вернет 123

0 голосов
/ 07 января 2018

У меня тоже такая же потребность, но она работает с JPA 2.0 и Hibernate 5.0.2:

SELECT p FROM MatchProfile p WHERE CONCAT(p.id, '') = :keyword

творит чудеса. Я думаю, что это работает и с LIKE.

0 голосов
/ 11 апреля 2012

Это также должно делать работу, но это касается SQL и не зависит от postgres.

select avg(cast(mynumber as numeric)) from my table
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...