Как привести строку к целому числу и иметь 0 в случае ошибки в приведении с PostgreSQL?

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

SELECT myfield::integer FROM mytable

доходность ERROR: invalid input syntax for integer: ""

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

11 ответов


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

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

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

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

SELECT CASE WHEN myfield~E'^\d+$' THEN myfield::integer ELSE 0 END FROM mytable;

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

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)

у меня была такая же потребность, и я нашел, что это хорошо работает для меня (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, когда значение для преобразования-только незначные символы, такие как "bad":

db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
     coalesce
----------
        0
(1 row)

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

(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 приведет к сообщению об ошибке.

надеюсь, что это помогает.


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

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


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

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

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

END
$func$  LANGUAGE plpgsql IMMUTABLE;

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

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


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


Я нашел следующий код простой и работает. Оригинальный ответ здесь 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)

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


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

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

с приведенным выше преобразованием вы заставляете Postgres преобразовывать эти значения снова и снова для каждой отдельной строки в каждом запросе для этой таблицы-это может серьезно снизить производительность при выполнении большого количества запросов к этому столбцу в этой таблице.


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

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

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


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

select avg(cast(mynumber as numeric)) from my table