NULL을 열 유형으로 캐스팅해야하는 이유는 무엇입니까?


10

대량 업데이트를 수행하는 코드를 생성하고 다음과 같은 SQL을 생성하는 도우미가 있습니다.

(활성 필드와 코어 필드는 모두 유형입니다 boolean)

UPDATE fields as t set "active" = new_values."active","core" = new_values."core"
FROM (values 
(true,NULL,3419),
(false,NULL,3420)
) as new_values("active","core","id") WHERE new_values.id = t.id;

그러나 다음과 같이 실패합니다.

ERROR: column "core" is of type boolean but expression is of type text

::booleannull 을 추가하여 작동시킬 수는 있지만 이상하게 보입니다. 왜 NULL이 유형으로 간주 TEXT됩니까?

또한 NULL을 캐스트 해야하는 유형을 알기 위해 코드를 약간의 재 작업해야하므로 캐스팅하기가 약간 까다 롭습니다 (열 및 값 목록은 현재 간단한 JSON 객체 배열에서 자동 생성됩니다) .

왜 이것이 필요하며 NULL 유형을 알기 위해 생성 코드를 필요로하지 않는보다 우아한 솔루션이 있습니까?

관련이 있다면 Node.JS를 통해 sequelize를 사용 하여이 작업을 수행하고 있지만 Postgres 명령 줄 클라이언트에서도 동일한 결과를 얻습니다.

답변:


16

이것은 흥미로운 발견입니다. 일반적으로 NULL은 다음과 같이 가정 된 데이터 유형이 없습니다.

SELECT pg_typeof(NULL);

 pg_typeof 
───────────
 unknown

VALUES테이블이 그림에 오면 변경됩니다 .

SELECT pg_typeof(core) FROM (
    VALUES (NULL)
) new_values (core);

 pg_typeof 
───────────
 text

이 동작은 https://doxygen.postgresql.org/parse__coerce_8c.html#l01373 의 소스 코드에 설명되어 있습니다 .

 /*
  * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
  * then resolve as type TEXT.  This situation comes up with constructs
  * like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END); SELECT 'foo'
  * UNION SELECT 'bar'; It might seem desirable to leave the construct's
  * output type as UNKNOWN, but that really doesn't work, because we'd
  * probably end up needing a runtime coercion from UNKNOWN to something
  * else, and we usually won't have it.  We need to coerce the unknown
  * literals while they are still literals, so a decision has to be made
  * now.
  */

(예, PostgreSQL 소스 코드는 우수한 주석 덕분에 이해하기 쉽고 대부분의 위치에 있습니다.)

그러나 탈출구는 다음과 같습니다. VALUES주어진 테이블의 모든 열과 일치하는 것을 항상 생성한다고 가정 해 봅시다 (다른 경우 아래 두 번째 참고 참조). 귀하의 예에서 작은 트릭이 도움이 될 수 있습니다.

SELECT (x).* FROM (VALUES ((TRUE, NULL, 1234)::fields)) t(x);

 active  core   id  
────────┼──────┼──────
 t             1234

여기서는 테이블 유형으로 캐스트 된 행 표현식 을 사용한 다음 다시 테이블로 추출합니다.

상기 내용을 토대로, 당신은 UPDATE같이 할 수

UPDATE fields AS t set active = (x).active, core = (x).core
FROM ( VALUES
           ((true, NULL, 3419)::fields),
           ((false, NULL, 3420)::fields)
     ) AS new_values(x) WHERE (x).id = t.id;

노트:

  • 사람의 가독성을 높이기 위해 큰 따옴표를 제거했지만 (열) 이름을 생성 할 때 도움을 줄 수 있습니다.
  • 열의 하위 집합 만 필요한 경우이 용도로 사용자 지정 유형만들있습니다 . 위에서와 동일한 방식으로 사용하십시오 (테이블에서 자동으로 생성 된 유형을 사용하고 후자의 행 구조를 유지합니다).

dbfiddle에서 작동하는 모든 것을 살펴보십시오 .


고마워, 그러나 이것은 나에게 흥미로워, 위의 코드는 Cannot cast type boolean to bigint in column 1(첫 번째 필드 진술 사이 ::에서 오류 지점) 생성 합니다.
ChristopherJ

1
@ ChristopherJ 대답은 호출 된 테이블 에 부울, 부울 및 int / bigint 유형의 fields3 열이 있다고 가정합니다 (active, core, id). 테이블에 더 많은 열 또는 다른 유형이 있거나 열이 다른 순서로 정의되어 있습니까?
ypercubeᵀᴹ

아, 고마워, 그래, 더 많은 열과 다른 순서가 있습니다. 감사합니다
ChristopherJ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.