regulares regexp_substr regexp_replace regexp_matches postgres expresiones example regex postgresql

regexp_replace - regexp_substr postgresql



isnumeric() con PostgreSQL (2)

Necesito determinar si una cadena dada se puede interpretar como un número (entero o punto flotante) en una declaración de SQL. Como en lo siguiente:

SELECT AVG(CASE WHEN x ~ ''^[0-9]*.?[0-9]*$'' THEN x::float ELSE NULL END) FROM test

Descubrí que la coincidencia de patrones de Postgres podría usarse para esto. Y entonces adapté la declaración dada en este lugar para incorporar números de coma flotante. Este es mi código:

WITH test(x) AS ( VALUES (''''), (''.''), (''.0''), (''0.''), (''0''), (''1''), (''123''), (''123.456''), (''abc''), (''1..2''), (''1.2.3.4'')) SELECT x , x ~ ''^[0-9]*.?[0-9]*$'' AS isnumeric FROM test;

La salida:

x | isnumeric ---------+----------- | t . | t .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f (11 rows)

Como puede ver, los dos primeros elementos (la cadena vacía '''' y el único período ''.'' ) Se clasifican erróneamente como de tipo numérico (que no lo son). No puedo acercarme más a esto en este momento. Cualquier ayuda apreciada!

Actualización Basándome en esta respuesta (y sus comentarios), adapté el patrón a:

WITH test(x) AS ( VALUES (''''), (''.''), (''.0''), (''0.''), (''0''), (''1''), (''123''), (''123.456''), (''abc''), (''1..2''), (''1.2.3.4''), (''1x234''), (''1.234e-5'')) SELECT x , x ~ ''^([0-9]+[.]?[0-9]*|[.][0-9]+)$'' AS isnumeric FROM test;

Lo que da:

x | isnumeric ----------+----------- | f . | f .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f 1x234 | f 1.234e-5 | f (13 rows)

Todavía hay algunos problemas con la notación científica y con números negativos, como veo ahora.


Como habrás notado, el método basado en expresiones regulares es casi imposible de hacer correctamente. Por ejemplo, su prueba dice que 1.234e-5 no es un número válido, cuando realmente lo es. Además, te perdiste números negativos. ¿Qué pasa si algo se parece a un número, pero cuando intenta almacenarlo causará un desbordamiento?

En cambio, recomendaría crear una función que intente realmente convertir a NUMERIC (o FLOAT si su tarea así lo requiere) y devuelve TRUE o FALSE dependiendo de si este lanzamiento fue exitoso o no.

Este código simulará completamente la función ISNUMERIC() :

CREATE OR REPLACE FUNCTION isnumeric(text) RETURNS BOOLEAN AS $$ DECLARE x NUMERIC; BEGIN x = $1::NUMERIC; RETURN TRUE; EXCEPTION WHEN others THEN RETURN FALSE; END; $$ STRICT LANGUAGE plpgsql IMMUTABLE;

Al activar esta función en sus datos obtiene los siguientes resultados:

WITH test(x) AS ( VALUES (''''), (''.''), (''.0''), (''0.''), (''0''), (''1''), (''123''), (''123.456''), (''abc''), (''1..2''), (''1.2.3.4''), (''1x234''), (''1.234e-5'')) SELECT x, isnumeric(x) FROM test; x | isnumeric ----------+----------- | f . | f .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f 1x234 | f 1.234e-5 | t (13 rows)

No solo es más correcto y fácil de leer, también funcionará más rápido si los datos fueran realmente un número.


Tu problema son los dos 0 o más elementos [0-9] en cada lado del punto decimal. Necesita usar un OR lógico | en la línea de identificación de número:

~''^([0-9]+/.?[0-9]*|/.[0-9]+)$''

Esto excluirá un punto decimal solo como un número válido.