it-swarm.com.de

isnumeric () mit PostgreSQL

Ich muss feststellen, ob eine bestimmte Zeichenfolge in einer SQL-Anweisung als Zahl (Ganzzahl oder Gleitkomma) interpretiert werden kann. Wie im folgenden:

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

Ich fand, dass Postgres ' Pattern Matching dafür verwendet werden könnte. Und so habe ich die in this place angegebene Anweisung angepasst, um Gleitkommazahlen aufzunehmen. Das ist mein Code:

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;

Die Ausgabe:

    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)

Wie Sie sehen können, die ersten beiden Elemente (die leere Zeichenfolge '' und die einzige Periode '.') sind als numerischer Typ falsch klassifiziert (was sie nicht sind). Dem kann ich momentan nicht näher kommen. Jede Hilfe dankbar!


Update Basierend auf dieser Antwort (und seinen Kommentaren) habe ich das Muster angepasst an:

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;

Welches gibt:

     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)

Wie ich jetzt sehe, gibt es immer noch einige Probleme mit der wissenschaftlichen Notation und mit negativen Zahlen.

32
moooeeeep

Wie Sie vielleicht bemerkt haben, ist es fast unmöglich, eine auf Regex basierende Methode korrekt auszuführen. Zum Beispiel sagt Ihr Test, dass 1.234e-5 Keine gültige Zahl ist, wenn es wirklich so ist. Außerdem haben Sie negative Zahlen verpasst. Was ist, wenn etwas wie eine Zahl aussieht, aber wenn Sie versuchen, es zu speichern, wird es einen Überlauf verursachen?

Stattdessen würde ich empfehlen, eine Funktion zu erstellen, die versucht, tatsächlich nach NUMERIC (oder FLOAT, wenn Ihre Aufgabe dies erfordert) umzuwandeln und je nach Bedarf TRUE oder FALSE zurückgibt ob diese Besetzung erfolgreich war oder nicht.

Dieser Code simuliert die Funktion ISNUMERIC() vollständig:

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;

Wenn Sie diese Funktion für Ihre Daten aufrufen, erhalten Sie folgende Ergebnisse:

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)

Es ist nicht nur korrekter und einfacher zu lesen, es funktioniert auch schneller, wenn die Daten tatsächlich eine Zahl waren.

70
mvp

Ihr Problem sind die zwei 0 oder mehr [0-9] Elemente auf jeder Seite des Dezimalpunkts. Sie müssen ein logisches OR | in der Nummernidentifikationszeile:

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

Dadurch wird nur ein Dezimalpunkt als gültige Zahl ausgeschlossen.

11
Mr Rho