Ошибка неверный синтаксис для типа numeric

SELECT Concat(npi.first_name, ' ', npi.middle_name, ' ', npi.last_name) AS othername, npi.nhif_persons_identity_serial, CASE WHEN npi.person_identity_number IS NULL THEN '' WHEN Trim(npi.
SELECT Concat(npi.first_name, ' ', npi.middle_name, ' ', npi.last_name) AS othername, npi.nhif_persons_identity_serial,
  CASE WHEN npi.person_identity_number IS NULL THEN '' WHEN Trim(npi.person_identity_number) = '' THEN ''
    ELSE npi.contact_email END, npi.biometric_member_number, npi.birth_certificate_number, npi.national_id_number, CASE
    WHEN npi.national_passport_number IS NULL THEN '' WHEN Trim(npi.national_passport_number) = '' THEN ''
    ELSE npi.national_passport_number END, CASE WHEN npi.national_pin_number IS NULL THEN ''
    WHEN Trim(npi.national_pin_number) = '' THEN '' ELSE npi.national_pin_number END, CASE
    WHEN npi.contact_email IS NULL THEN '' WHEN Trim(npi.contact_email) = '' THEN '' ELSE npi.contact_email END, CASE
    WHEN npi.address_district IS NULL THEN '' WHEN Trim(npi.address_district) = '' THEN '' ELSE npi.address_district
  END, CASE WHEN npi.address_location IS NULL THEN '' WHEN Trim(npi.address_location) = '' THEN ''
    ELSE npi.address_location END, CASE WHEN npi.address_county IS NULL THEN ''
    WHEN Trim(npi.address_county) = '' THEN '' ELSE npi.address_county END, CASE WHEN npi.work_code IS NULL THEN ''
    WHEN Trim(npi.work_code) = '' THEN '' ELSE npi.work_code END, CASE WHEN npi.address_physical IS NULL THEN ''
    WHEN Trim(npi.address_physical) = '' THEN '' ELSE npi.address_physical
  END
  , npi.date_of_birth  , npi.date_registered, npi.date_added,CASE WHEN npi.gender IS NULL THEN ''
    WHEN Trim(npi.gender) = '' THEN '' ELSE npi.gender
  END
  , npi.first_name, npi.middle_name, npi.last_name,

   CASE WHEN npi.marital_status IS NULL THEN ''
    WHEN Trim(npi.marital_status) = '' THEN '' ELSE npi.marital_status
  END
  ,CASE WHEN npi.contact_address IS NULL THEN ''
    WHEN Trim(npi.contact_address) = '' THEN '' ELSE npi.contact_address
  END
  ,CASE WHEN npi.contact_telephone IS NULL THEN ''
    WHEN Trim(npi.contact_telephone) = '' THEN '' ELSE npi.contact_telephone
  END
  ,CASE WHEN npi.contact_mobile IS NULL THEN ''
    WHEN Trim(npi.contact_mobile) = '' THEN '' ELSE npi.contact_mobile
  END
  ,CASE WHEN npi.mobile_contact_detail IS NULL THEN ''
    WHEN Trim(npi.mobile_contact_detail) = '' THEN '' ELSE npi.mobile_contact_detail
  END
  ,CASE WHEN npi.temp_member_number IS NULL THEN ''
     ELSE npi.temp_member_number
  END
  ,CASE WHEN npi.national_nhif_number IS NULL THEN ''
    WHEN Trim(npi.national_nhif_number) = '' THEN '' ELSE npi.national_nhif_number
  END
  ,CASE WHEN nbci.facial_full_image IS NULL THEN ''
    WHEN Trim(nbci.facial_full_image) = '' THEN '' ELSE nbci.facial_full_image
  END
  ,CASE WHEN nbci.facial_scaled_image IS NULL THEN ''
    WHEN Trim(nbci.facial_scaled_image) = '' THEN '' ELSE nbci.facial_scaled_image
  END

FROM nhif_persons_identities npi
  INNER JOIN nhif_biometric_cards nbc ON npi.nhif_persons_identity_serial = nbc.nhif_persons_identity_serial
  INNER JOIN nhif_biometric_card_images nbci ON nbci.nhif_biometric_card_serial = nbc.nhif_biometric_card_serial
WHERE npi.nhif_persons_identity_serial = 293182

Am selecting data from database and if the column doesn’t contain any value it output «blank» , Though am running into the error below when i execute the query i have tried to search but the answers i have got doesn’t work.
It seems its the error with numeric.

ERROR:  invalid input syntax for type numeric: ""
LINE 35:   ,CASE WHEN npi.temp_member_number IS NULL THEN ''
                                                          ^

********** Error **********

ERROR: invalid input syntax for type numeric: ""
SQL state: 22P02
Character: 2318
SELECT Concat(npi.first_name, ' ', npi.middle_name, ' ', npi.last_name) AS othername, npi.nhif_persons_identity_serial,
  CASE WHEN npi.person_identity_number IS NULL THEN '' WHEN Trim(npi.person_identity_number) = '' THEN ''
    ELSE npi.contact_email END, npi.biometric_member_number, npi.birth_certificate_number, npi.national_id_number, CASE
    WHEN npi.national_passport_number IS NULL THEN '' WHEN Trim(npi.national_passport_number) = '' THEN ''
    ELSE npi.national_passport_number END, CASE WHEN npi.national_pin_number IS NULL THEN ''
    WHEN Trim(npi.national_pin_number) = '' THEN '' ELSE npi.national_pin_number END, CASE
    WHEN npi.contact_email IS NULL THEN '' WHEN Trim(npi.contact_email) = '' THEN '' ELSE npi.contact_email END, CASE
    WHEN npi.address_district IS NULL THEN '' WHEN Trim(npi.address_district) = '' THEN '' ELSE npi.address_district
  END, CASE WHEN npi.address_location IS NULL THEN '' WHEN Trim(npi.address_location) = '' THEN ''
    ELSE npi.address_location END, CASE WHEN npi.address_county IS NULL THEN ''
    WHEN Trim(npi.address_county) = '' THEN '' ELSE npi.address_county END, CASE WHEN npi.work_code IS NULL THEN ''
    WHEN Trim(npi.work_code) = '' THEN '' ELSE npi.work_code END, CASE WHEN npi.address_physical IS NULL THEN ''
    WHEN Trim(npi.address_physical) = '' THEN '' ELSE npi.address_physical
  END
  , npi.date_of_birth  , npi.date_registered, npi.date_added,CASE WHEN npi.gender IS NULL THEN ''
    WHEN Trim(npi.gender) = '' THEN '' ELSE npi.gender
  END
  , npi.first_name, npi.middle_name, npi.last_name,

   CASE WHEN npi.marital_status IS NULL THEN ''
    WHEN Trim(npi.marital_status) = '' THEN '' ELSE npi.marital_status
  END
  ,CASE WHEN npi.contact_address IS NULL THEN ''
    WHEN Trim(npi.contact_address) = '' THEN '' ELSE npi.contact_address
  END
  ,CASE WHEN npi.contact_telephone IS NULL THEN ''
    WHEN Trim(npi.contact_telephone) = '' THEN '' ELSE npi.contact_telephone
  END
  ,CASE WHEN npi.contact_mobile IS NULL THEN ''
    WHEN Trim(npi.contact_mobile) = '' THEN '' ELSE npi.contact_mobile
  END
  ,CASE WHEN npi.mobile_contact_detail IS NULL THEN ''
    WHEN Trim(npi.mobile_contact_detail) = '' THEN '' ELSE npi.mobile_contact_detail
  END
  ,CASE WHEN npi.temp_member_number IS NULL THEN ''
     ELSE npi.temp_member_number
  END
  ,CASE WHEN npi.national_nhif_number IS NULL THEN ''
    WHEN Trim(npi.national_nhif_number) = '' THEN '' ELSE npi.national_nhif_number
  END
  ,CASE WHEN nbci.facial_full_image IS NULL THEN ''
    WHEN Trim(nbci.facial_full_image) = '' THEN '' ELSE nbci.facial_full_image
  END
  ,CASE WHEN nbci.facial_scaled_image IS NULL THEN ''
    WHEN Trim(nbci.facial_scaled_image) = '' THEN '' ELSE nbci.facial_scaled_image
  END

FROM nhif_persons_identities npi
  INNER JOIN nhif_biometric_cards nbc ON npi.nhif_persons_identity_serial = nbc.nhif_persons_identity_serial
  INNER JOIN nhif_biometric_card_images nbci ON nbci.nhif_biometric_card_serial = nbc.nhif_biometric_card_serial
WHERE npi.nhif_persons_identity_serial = 293182

Am selecting data from database and if the column doesn’t contain any value it output «blank» , Though am running into the error below when i execute the query i have tried to search but the answers i have got doesn’t work.
It seems its the error with numeric.

ERROR:  invalid input syntax for type numeric: ""
LINE 35:   ,CASE WHEN npi.temp_member_number IS NULL THEN ''
                                                          ^

********** Error **********

ERROR: invalid input syntax for type numeric: ""
SQL state: 22P02
Character: 2318

Я пытаюсь отладить функцию, созданную не мной (dms2dd). Я сделал свою собственную тестовую функцию (см. Ниже) и свел мою проблему к определенной строке / значению.

Если я запускаю следующий запрос:

SELECT "Lat", "Long", test_dolf("Lat"), test_dolf("Long") FROM pawikan WHERE "Lat" IS NOT NULL AND "Long" IS NOT NULL ORDER BY index LIMIT 1 OFFSET 29130

Я получаю следующий вывод:

'N6° 6' 9.4824"';'E118° 26' 49.1172'' ';'9.4824';'49.1172'

Это именно то, что я ожидаю. Но со следующим запросом:

SELECT "Lat", "Long", CAST(test_dolf("Lat") as numeric), test_dolf("Long") FROM pawikan WHERE "Lat" IS NOT NULL AND "Long" IS NOT NULL ORDER BY index  LIMIT 1 OFFSET 29130

Я получаю ошибку

ERROR: invalid input syntax for type numeric: ""
SQL state: 22P02

Ошибка предполагает, что значение varchar, которое я пытался привести к числовому, пусто, но, как вы можете видеть из предыдущего запроса, это не так. Это просто действительный числовой varchar. На самом деле, если я копирую-вставляю значение и запускаю:

SELECT CAST('9.4824' AS numeric);

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

SELECT "Lat", "Long", test_dolf("Lat") as lat_sec, test_dolf("Long") as long_sec INTO dms2dd_test FROM pawikan WHERE "Lat" IS NOT NULL AND "Long" IS NOT NULL ORDER BY index LIMIT 11 OFFSET 29120

А затем выдать

SELECT CAST(long_sec as numeric), CAST(lat_sec AS numeric) FROM dms2dd_test;

Это полностью работает. Даже это работает просто отлично:

SELECT test_dolf(E'N6° 6' 9.4824"')::numeric as lat_sec

Так что здесь не так? Похоже, что во втором запросе, где я приводил числовые значения, моей функции передается другое значение, но я протестировал столбец сортировки (индекс) и он содержит только уникальные bigints.

Это код для функции test_dolf:

CREATE OR REPLACE FUNCTION public.test_dolf(strdegminsec character varying)
  RETURNS varchar AS
$BODY$
    DECLARE
       i               numeric;
       intDmsLen       numeric;          -- Length of original string
       strCompassPoint Char(1);
       strNorm         varchar(16) = ''; -- Will contain normalized string
       strDegMinSecB   varchar(100);
       blnGotSeparator integer;          -- Keeps track of separator sequences
       arrDegMinSec    varchar[];        -- TYPE stringarray is table of varchar(2048) ;
       strChr          Char(1);
    BEGIN
       strDegMinSec := regexp_replace(replace(strdegminsec,E'''','"'),' "([0-9]+)',E' \1"');
       -- Remove leading and trailing spaces
       strDegMinSecB := REPLACE(strDegMinSec,' ','');
       intDmsLen := Length(strDegMinSecB);

       blnGotSeparator := 0; -- Not in separator sequence right now

       -- Loop over string, replacing anything that is not a digit or a
       -- decimal separator with
       -- a single blank
       FOR i in 1..intDmsLen LOOP
          -- Get current character
          strChr := SubStr(strDegMinSecB, i, 1);
          -- either add character to normalized string or replace
          -- separator sequence with single blank         
          If strpos('0123456789,.', strChr) > 0 Then
             -- add character but replace comma with point
             If (strChr <> ',') Then
                strNorm := strNorm || strChr;
             Else
                strNorm := strNorm || '.';
             End If;
             blnGotSeparator := 0;
          ElsIf strpos('neswNESW',strChr) > 0 Then -- Extract Compass Point if present
            strCompassPoint := strChr;
          Else
             -- ensure only one separator is replaced with a blank -
             -- suppress the rest
             If blnGotSeparator = 0 Then
                strNorm := strNorm || ' ';
                blnGotSeparator := 0;
             End If;
          End If;
       End Loop;

       -- Split normalized string into array of max 3 components
       arrDegMinSec := string_to_array(strNorm, ' ');
       return arrDegMinSec[3];
    End 
$BODY$
  LANGUAGE plpgsql IMMUTABLE
  COST 100;

2 ответа

Лучший ответ

Я понял, в чем проблема. Похоже, что postgresql, хотя я делаю LIMIT и OFFSET, все еще вызывает функции в select для других строк за пределами этого фрейма.

Я понял это, поместив код, который вызвал исключение, в мою функцию и перехватив полученную ошибку, и вызвал ошибку NOTICE, когда это исключение произошло (см. Функцию ниже, в частности, блок BEGIN EXCEPTION END в конце функции). Уведомление отображается как предупреждение, но не останавливает выполнение кода. Внезапно оказалось, что функция вызывается не только для строки, к которой я ожидал, но также и для целого ряда других строк. Это совсем не то, что я ожидал, и для меня это немного противоречит интуиции, но я предполагаю, что именно так должен работать postgresql.

Поскольку перехват исключений в postgresql довольно дорог, я думаю, мне нужно добавить тест, который в первую очередь предотвращает исключение (я мог бы проверить длину arrDegMinSec и значение элементов 1-3 этого массива и вернуть NULL в случае недопустимых значений.

CREATE OR REPLACE FUNCTION public.test_dolf(strdegminsec character varying)
  RETURNS numeric AS
$BODY$
    DECLARE
       i               numeric;
       intDmsLen       numeric;          -- Length of original string
       strCompassPoint Char(1);
       strNorm         varchar(16) = ''; -- Will contain normalized string
       strDegMinSecB   varchar(100);
       blnGotSeparator integer;          -- Keeps track of separator sequences
       arrDegMinSec    varchar[];        -- TYPE stringarray is table of varchar(2048) ;
       strChr          Char(1);
       retval          numeric;
    BEGIN

       strDegMinSec := regexp_replace(replace(strdegminsec,E'''','"'),' "([0-9]+)',E' \1"');
       -- Remove leading and trailing spaces
       strDegMinSecB := REPLACE(strDegMinSec,' ','');
       intDmsLen := Length(strDegMinSecB);

       blnGotSeparator := 0; -- Not in separator sequence right now

       -- Loop over string, replacing anything that is not a digit or a
       -- decimal separator with
       -- a single blank
       FOR i in 1..intDmsLen LOOP
          -- Get current character
          strChr := SubStr(strDegMinSecB, i, 1);
          -- either add character to normalized string or replace
          -- separator sequence with single blank         
          If strpos('0123456789,.', strChr) > 0 Then
             -- add character but replace comma with point
             If (strChr <> ',') Then
                strNorm := strNorm || strChr;
             Else
                strNorm := strNorm || '.';
             End If;
             blnGotSeparator := 0;
          ElsIf strpos('neswNESW',strChr) > 0 Then -- Extract Compass Point if present
            strCompassPoint := strChr;
          Else
             -- ensure only one separator is replaced with a blank -
             -- suppress the rest
             If blnGotSeparator = 0 Then
                strNorm := strNorm || ' ';
                blnGotSeparator := 0;
             End If;
          End If;
       End Loop;
       -- Split normalized string into array of max 3 components
       arrDegMinSec := string_to_array(strNorm, ' ');
       BEGIN
          retval := arrDegMinSec[3]::numeric;
          return retval;
       EXCEPTION
          WHEN SQLSTATE '22P02' THEN
             RAISE NOTICE 'Incorrect value %', strDegMinSec;
             RETURN NULL;
       END;
    End 
$BODY$
  LANGUAGE plpgsql IMMUTABLE
  COST 100;

< Сильный > ИЗМЕНИТЬ

Предоставлено @ michel.milezzi. Другим решением, не требующим изменения функции, является изменение вызова функции в запросе на

CAST(NULLIF(test_dolf("Lat"), '') as numeric)

И действительно, как подсказывает @abelisto, я мог бы также поместить запрос в подзапрос и преобразовать его в числовой в основном запросе так:

SELECT "Lat", "Long", CAST(test_dolf("Lat") as numeric), test_dolf("Long") FROM (SELECT * FROM pawikan WHERE "Lat" IS NOT NULL AND "Long" IS NOT NULL ORDER BY index LIMIT 1 OFFSET 29130) as t

Это действительно предотвратило бы проблему, которая действительно упростила бы процесс отладки.

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


1

Dolf Andringa
25 Июн 2017 в 03:38

Ошибка, которую вы получаете, заключается в следующем:

ERROR: invalid input syntax for type numeric: ""

Итак, он пытается привести пустую строку к числовому значению. Как насчет использования NULLIF функции для решения этой проблемы?

SELECT "Lat", "Long", CAST(NULLIF(test_dolf("Lat"), '') as numeric), test_dolf("Long") FROM pawikan WHERE "Lat" IS NOT NULL AND "Long" IS NOT NULL ORDER BY index  LIMIT 1 OFFSET 29130;

Также вы можете захотеть увидеть план выполнения, чтобы понять эту проблему. Может случиться так, что LIMIT и OFFSET выполняются сразу после приведения. Это объясняет, почему вы не видите строку с пустой строкой.


РЕДАКТИРОВАТЬ

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


1

Michel Milezzi
24 Июн 2017 в 13:49

Что сказал @Laurenz.

Но почему «странное» сообщение об ошибке?

Ваша синтаксическая ошибка (0,8 вместо 0.8 в cour_g=(((upox_sum+epil_sum)/2)*0,8);) запутано добавленными, безвозмездными скобками, которые интерпретируются как конструкторы ROW — ключевое слово ROW — дополнительный шум.

Что вы видите в сообщении об ошибке:

ERROR:  invalid input syntax for type numeric: "(0.0000000000000000,8)"

… представляет собой выражение строки, состоящее из полей: 0.0000000000000000 и 8.
Без вводящих в заблуждение круглых скобок назначение:

cour_g=((upox_sum+epil_sum)/2)*0,8;  -- still incorrect: 0,8

… произвело бы другое (более открытое) сообщение об ошибке:

ERROR:  query "SELECT ((upox_sum+epil_sum)/2)*0,8" returned 2 columns

потому что запятая (,) интерпретируется как разделитель столбцов.

Это сообщение также показывает, что PL/pgSQL оценивает каждое голое выражение с помощью (основного и быстрого) SELECT внутренне. Язык в основном представляет собой оболочку вокруг ядра SQL. Это объясняет, почему присвоения немного дороже, чем можно ожидать от других PL.

Ваш исходный оператор эквивалентен более подробному синтаксису ROW:

cour_g=ROW(((upox_sum + epil_sum)/2)*0,8);  -- still incorrect: 0,8

… который функционально эквивалентен:

cour_g=(SELECT ROW(((upox_sum + epil_sum)/2)*0,8));  -- still incorrect: 0,8

Оба результата приводят к тому же сообщению об ошибке, которое вы наблюдали.

Или:

cour_g=(SELECT ((upox_sum + epil_sum)/2)*0,8);  -- still incorrect: 0,8

С другим (уже более показательным) сообщением об ошибке:

ERROR:  subquery must return only one column

Требуются скобки вокруг вложенного явного SELECT. Если вы отбросите ненужный SELECT, оставьте с ним скобки-скобки или они интерпретируются как конструктор ROW (с или без шумового слова ROW).

Поэтому используйте:

cour_g := (upox_sum + epil_sum) / 2 * 0.8;

((upox_sum+epil_sum)/2) вокруг ((upox_sum+epil_sum)/2) также излишни, потому что * и / имеют один и тот же приоритет оператора и в любом случае оцениваются слева направо. Но в этом случае добавленные круглые скобки представляют собой безопасный шум (что может или не поможет читаемости).

Или упростить:

cour_g := (upox_sum + epil_sum) * 0.4;

Тем не менее, поскольку присвоения сравнительно дороги в PL/pgSQL, попробуйте применить стиль программирования с меньшим количеством назначений.
И не все безвозмездные круглые скобки безвредны.

Понравилась статья? Поделить с друзьями:
  • Ошибка неверный синтаксис для типа integer
  • Ошибка неверный синтаксис для типа bigint
  • Ошибка не удалось загрузить метаданные для репозитория
  • Ошибка не удалось загрузить изображение япикс
  • Ошибка неверный домен ключа что это значит