PostgreSQL로 캐스팅 할 때 오류가 발생하면 문자열을 정수로 캐스팅하고 0을 어떻게 사용합니까?


128

PostgreSQL에는 varchar 열이있는 테이블이 있습니다. 데이터는 정수로 가정되며 쿼리에서 정수 유형으로 필요합니다. 일부 값은 빈 문자열입니다. 다음과 같은:

SELECT myfield::integer FROM mytable

수확량 ERROR: invalid input syntax for integer: ""

postgres에서 캐스트하는 동안 오류가 발생하면 캐스트를 쿼리하고 0을 어떻게 가질 수 있습니까?

답변:


161

나는 비슷한 문제를 겪고 있었지만 함수의 오버 헤드를 원하지 않았다. 나는 다음과 같은 쿼리를 생각해 냈다.

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

Postgres는 조건부 바로 가기를 사용하므로 정수가 아닌 정수를 적중하지 않아야합니다. 또한 NULL 값을 처리합니다 (정규 표현식과 일치하지 않습니다).

선택하지 않고 0을 원하면 CASE 문이 작동합니다.

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

14
나는 매튜의 제안을 따르는 것이 좋습니다. 이 솔루션에는 숫자처럼 보이지만 정수에 넣을 수있는 최대 값보다 큰 문자열에 문제가 있습니다.
pilif

4
나는 두 번째 필립의 의견. 최대 값은 버그가 발생하기를 기다리는 것입니다. 오류가 발생하지 않는 것은 데이터가 유효하지 않을 때 오류가 발생하지 않는 것입니다. 이 대답은 해결되지 않습니다. 고마워 매튜! 훌륭한 일!
Shawn Kovac

3
Matthew의 대답만큼이나, 나는 데이터를 확인하기 위해 빠르고 더러운 처리 방법이 필요했습니다. 또한 SQL에서 함수를 정의 할 때 내 지식이 부족하다는 것을 인정합니다. 1과 5 사이의 숫자에만 관심이 있었으므로 정규 표현식을로 변경했습니다 E'\\d{1,5}$'.
Bobort

3
예, 그렇습니다.이 솔루션은 비교적 빠르고 더티이지만 내 경우에는 내가 가지고있는 데이터와 테이블이 비교적 짧다는 것을 알았습니다. 전체 함수를 작성 (및 디버깅)하는 것보다 훨씬 쉽습니다. {1,5}오버플로가 걱정된다면 @Bobort의 자릿수 제한은 좋은 아이디어 일 수 있지만 더 큰 숫자를 가리면 테이블을 변환 할 때 문제가 발생할 수 있습니다. 개인적으로 쿼리 오류가 먼저 발생하고 내 "정수"중 일부가 문제가 있음을 알고 싶습니다 ( E'\\d{6,}$'먼저 선택하여 선택할 수도 있음 ).
Anthony Briggs

1
@Anthony Briggs : myfield에 " '"또는 ","또는 "."또는'- '가 포함 된 경우 작동하지 않습니다.
Stefan Steiger

100

예외 블록을 사용할 있는 자체 변환 함수를 만들 수도 있습니다 .

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)

8
허용 된 답변과 달리이 솔루션은 정수에 맞지 않는 너무 큰 숫자를 똑같이 잘 처리 할 수 ​​있으며 일반적인 경우 유효성 검사 작업이 없기 때문에 더 빠를 수도 있으므로 더 정확합니다 (= 유효한 문자열 )
pilif

in 문 에서 함수 사용하여 특정 필드에서 문자열을 정수로 어떻게 캐스팅 INSERT합니까?
sk

27

나는 같은 종류의 요구가 있었고 이것이 나를 위해 잘 작동한다는 것을 알았습니다 (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)

그런 다음 "b3ad5"와 같은 text / varchar 값도 숫자를 제공합니다

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

Chris Cogdon이 "bad"(숫자 없음)와 같은 경우를 포함하여 모든 경우에 0을 제공하지 않는 솔루션에 대한 우려를 해결하기 위해 다음과 같이 조정했습니다.

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

변환 할 값이 숫자가 아닌 문자 (예 : "bad") 인 경우 0을 제공한다는 점을 제외하고는 더 간단한 솔루션과 유사하게 작동합니다.

db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
     coalesce
----------
        0
(1 row)

'0'이 필요한 이유 || ? 문서에서 : "COALESCE 함수는 널이 아닌 첫 번째 인수를 리턴합니다." 따라서 값으로 null이 있으면 Coalesce가이를 제거합니다.
Amala

@Amala True. 잘 잡았다. 편집했습니다.
ghbarratt

1
입력이 정수 또는 NULL 인 경우에만 솔루션이 작동합니다. 질문은 모든 종류의 입력을 변환하도록 요청했으며 변환 할 수없는 경우 0을 사용하십시오.
Chris Cogdon

@ChrisCogdon 변환 할 값이 "변환 불가능"인 경우 항상 0을 제공하지 않는 문제를 해결하기 위해 솔루션에 추가했습니다. 이 수정 된 솔루션 버전은 숫자가없는 문자열이 변환 할 값으로 제공 될 때 0을 반환합니다.
ghbarratt

22

이것은 다소 해킹 일 수 있지만 우리의 경우에는 작업이 완료되었습니다.

(0 || myfield)::integer

설명 (Postgres 8.4에서 테스트) :

위에서 언급 한 표현식은 빈 문자열의 NULLNULL 값 myfield0빈 문자열에 대해 산출 합니다 (이 정확한 동작은 사용 사례에 맞거나 맞지 않을 수 있음).

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오류 메시지가 나타납니다.

도움이 되었기를 바랍니다.


3

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

PostgreSQL로 작업 한 적이 없지만 SELECT 쿼리에서 IF 구문의 올바른 구문에 대해 매뉴얼 을 확인했습니다 .


그것은 지금 그대로 테이블에서 작동합니다. 나는 미래에 숫자가 아닌 값을 포함 할 수 있다고 조금 두려워합니다. try / catch와 같은 솔루션을 선호했지만 트릭을 수행합니다. 감사.
silviot

아마 정규 표현식 postgresql.org/docs/8.4/interactive/functions-matching.html을 사용할 수 있지만 비용이 많이들 수 있습니다. 그것이 해결책이라면 답변도 받아보십시오 :)
Jan Hančič

3

@Matthew의 답변 이 좋습니다. 그러나 더 간단하고 빠를 수 있습니다. 그리고 질문은 빈 문자열 ( '') 을로 변환 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유효하지 않은 다른 입력을 반환 합니다 . 모든 데이터 유형 변환에
쉽게 적용 할 수 있습니다 .

예외 블록을 입력하면 비용이 훨씬 많이 듭니다. 빈 문자열이 일반적인 경우 예외를 발생시키기 전에 해당 사례를 파악하는 것이 좋습니다.
빈 문자열이 매우 드문 경우 테스트를 예외 절로 옮기는 데 비용이 듭니다.


1
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


정규식 대 문자열 기능에 대한 성능 테스트를 수행 했습니까? 또한 이것은 어떻게 null을 처리합니까? 예상대로 0 또는 NULL을 반환합니까? 감사!
vol7ron


1

SUBSTRING은 경우에 따라 도움이 될 수 있습니다. int의 크기를 제한 할 수 있습니다.

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

0

데이터가 정수 여야하고 정수로만 필요한 값이라면, 전체 마일리지로 이동하여 열을 정수 열로 변환하지 않겠습니까?

그런 다음 데이터가 테이블에 삽입되는 시스템 지점에서 잘못된 값을 0으로 한 번만 변환 할 수 있습니다.

위의 변환을 사용하면 Postgres가 해당 테이블에 대한 각 쿼리의 각 단일 행에 대해 해당 값을 반복해서 변환해야합니다.이 테이블 의이 열에 대해 많은 쿼리를 수행하면 성능이 크게 저하 될 수 있습니다.


원칙적으로는 맞지만이 특정 시나리오에서는 응용 프로그램에서 단일 느린 쿼리를 최적화해야합니다. 데이터 입력을 처리하는 코드가 어떻게 작동하는지 모르겠습니다. 만지고 싶지 않습니다. 지금까지 다시 작성된 쿼리가 작동하지만 예기치 않은 경우 중단되지 않기를 바랍니다. 가장 합리적인 것 같더라도 응용 프로그램을 다시 설계하는 것은 옵션이 아닙니다.
silviot

0

다음 기능은

  • error_result캐스팅 할 수없는 결과에 대해 기본값 ( )을 사용하십시오. abc또는999999999999999999999999999999999999999999
  • 유지 nullnull
  • 공백 및 기타 공백을 제거합니다.
  • 유효한 값으로 캐스팅 된 값 bigints은 다음과 비교 lower_bound됩니다. 예 : 양수 값만 적용
CREATE OR REPLACE FUNCTION cast_to_bigint(text) 
RETURNS BIGINT AS $$
DECLARE big_int_value BIGINT DEFAULT NULL;
DECLARE error_result  BIGINT DEFAULT -1;
DECLARE lower_bound   BIGINT DEFAULT 0;
BEGIN
    BEGIN
        big_int_value := CASE WHEN $1 IS NOT NULL THEN GREATEST(TRIM($1)::BIGINT, lower_bound) END;
    EXCEPTION WHEN OTHERS THEN
        big_int_value := error_result;
    END;
RETURN big_int_value;
END;

-1

또한 동일한 요구 사항이 있지만 JPA 2.0 및 Hibernate 5.0.2에서 작동합니다.

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

경이로움 LIKE에서도 작동한다고 생각합니다.


-3

이것은 또한 작업을 수행해야하지만 이것은 SQL 전반에 적용되며 postgres와 관련이 없습니다.

select avg(cast(mynumber as numeric)) from my table
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.