¿Cómo puedo convertir una cadena a integer y tener 0 en caso de error en el cast con PostgreSQL?


En PostgreSQL tengo una tabla con una columna varchar. Se supone que los datos son enteros y lo necesito en tipo entero en una consulta. Algunos valores son cadenas vacías. Lo siguiente:

SELECT myfield::integer FROM mytable

Rinde ERROR: invalid input syntax for integer: ""

¿Cómo puedo consultar un cast y tener 0 en caso de error durante el cast en postgres?

Author: jabko87, 2010-01-18

11 answers

Yo solo estaba luchando con un problema similar, pero no quería la sobrecarga de una función. Se me ocurrió la siguiente pregunta:

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

Postgres acorta sus condicionales, por lo que no debe obtener ningún número no entero que golpee su ::integer cast. También maneja valores NULOS (no coincidirán con la expresión regular).

Si desea ceros en lugar de no seleccionar, entonces una sentencia CASE debería funcionar:

SELECT CASE WHEN myfield~E'^\\d+$' THEN myfield::integer ELSE 0 END FROM mytable;
 122
Author: Anthony Briggs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-24 03:07:55

También puede crear su propia función de conversión, dentro de la cual puede usar bloques de excepción:

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;

Pruebas:

=# 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)
 74
Author: Matthew Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-19 17:52:03

Tenía el mismo tipo de necesidad y encontré que esto funcionaba bien para mí (postgres 8.4):

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

Algunos casos de prueba para demostrar:

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"

Si necesita manejar la posibilidad de que el campo tenga texto no numérico (como "100bad"), puede usar regexp_replace para eliminar los caracteres no numéricos antes de la emisión.

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

Entonces los valores de text/varchar como "b3ad5" también darán números

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

Para abordar la preocupación de Chris Cogdon con la solución de no dar 0 para todos casos, incluyendo un caso como "malo" (sin caracteres de dígitos en absoluto), hice esta declaración ajustada:

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

Funciona de manera similar a las soluciones más simples, excepto que dará 0 cuando el valor a convertir es solo caracteres no dígitos, como "malo":

db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
     coalesce
----------
        0
(1 row)
 23
Author: ghbarratt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-25 16:01:18

Esto podría ser algo así como un truco, pero hizo el trabajo en nuestro caso:

(0 || myfield)::integer

Explicación (Probado en Postgres 8.4):

La expresión mencionada anteriormente produce NULL para valores NULOS en myfield y 0 para cadenas vacías (este comportamiento exacto puede o no ajustarse a su caso de uso).

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

Datos de prueba:

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');

La consulta producirá el siguiente resultado:

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

Mientras que seleccionar solo values::integer dará lugar a un mensaje de error.

Espero que esto ayudar.

 16
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-19 13:51:28

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

Nunca he trabajado con PostgreSQL pero he comprobado el manual para la sintaxis correcta de las sentencias IF en las consultas SELECT.

 3
Author: Jan Hančič,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-17 21:25:44

@La respuesta de Mateo es buena. Pero puede ser más simple y más rápido. Y la pregunta pide convertir cadenas vacías ('') a 0, pero no otra entrada de" sintaxis de entrada no válida "o" fuera de rango":

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;

Devuelve 0 para una cadena vacía y NULL para cualquier otra entrada no válida.
Se puede adaptar fácilmente para cualquier conversión de tipo de datos .

Introducir un bloque de excepción es sustancialmente más caro. Si las cadenas vacías son comunes tiene sentido captar ese caso antes de plantear una excepción.
Si las cadenas vacías son muy raras, vale la pena mover la prueba a la cláusula de excepción.

 3
Author: Erwin Brandstetter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:24
CREATE OR REPLACE FUNCTION parse_int(s TEXT) RETURNS INT AS $$
BEGIN
  RETURN regexp_replace(('0' || s), '[^\d]', '', 'g')::INT;
END;
$$ LANGUAGE plpgsql;

Esta función siempre devolverá 0 si no hay dígitos en la cadena de entrada.

SELECT parse_int('test12_3test');

Volverá 123

 1
Author: Oleg Mikhailov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-26 08:24:38

Encontré el siguiente código fácil y funcional. La respuesta original está aquí 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)

Espero que ayude

 1
Author: Ashish Rana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-02 13:18:58

Si se supone que los datos son enteros, y solo necesita esos valores como enteros, ¿por qué no va toda la milla y convierte la columna en una columna entera?

Entonces podría hacer esta conversión de valores ilegales en ceros solo una vez, en el punto del sistema donde los datos se insertan en la tabla.

Con la conversión anterior, está forzando a Postgres a convertir esos valores una y otra vez para cada fila en cada consulta para esa tabla - esto puede seriamente degrada el rendimiento si realiza muchas consultas en esta columna de esta tabla.

 0
Author: Bandi-T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-18 22:01:51

También tengo la misma necesidad pero eso funciona con JPA 2.0 e Hibernate 5.0.2:

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

Hace maravillas. Creo que funciona con IGUAL también.

 0
Author: Hendy Irawan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-07 12:15:31

Esto también debería hacer el trabajo, pero esto es a través de SQL y no es específico de postgres.

select avg(cast(mynumber as numeric)) from my table
 -3
Author: ronak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-11 17:31:13