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

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

SELECT myfield::integer FROM mytable

дает ERROR: invalid input syntax for integer: ""

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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)

Ответ 3

У меня была такая же потребность, и я нашел, что это хорошо работает для меня (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)

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

@Matthew answer - это хорошо. Но это может быть проще и быстрее. И этот вопрос просит преобразовать пустые строки ('') в 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 для любого другого недопустимого ввода.
Его можно легко адаптировать для любого преобразования типов данных.

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

Ответ 7

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

Ответ 8

Я нашел следующий код легким и работоспособным. Оригинальный ответ здесь https://www.postgresql.org/message-id/[email protected]

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)

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

Ответ 9

SUBSTRING может помочь в некоторых случаях, вы можете ограничить размер int.

SELECT CAST(SUBSTRING('X12312333333333', '([\d]{1,9})') AS integer);

Ответ 10

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

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

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

Ответ 11

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

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

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

Ответ 12

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

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