upsert에서 충돌하는 행의 ID를 얻는 방법은 무엇입니까?


18

(uuid) 및 (text)의 tag두 열 이있는 테이블 이 있습니다. 이제 테이블에 새 태그를 삽입하고 싶지만 태그가 이미 존재 하는 경우 기존 레코드 를 가져오고 싶습니다 .idnameid

나는 다음 ON CONFLICT DO NOTHING과 함께 사용할 수 있다고 가정 했다 RETURNING "id".

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";

그러나 이름이 "foo"인 태그가 이미 존재하면 빈 결과 집합이 반환됩니다.

그런 다음 noop DO UPDATE절 을 사용하도록 쿼리를 변경했습니다 .

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";

의도 된대로 작동하지만 이름을 이미 기존 값으로 설정하기 때문에 다소 혼란 스럽습니다.

이것이이 문제를 해결하는 방법입니까, 아니면 내가 놓친 간단한 접근법이 있습니까?


당신은 시도 returning excluded.id했습니까?
a_horse_with_no_name

@a_horse_with_no_name ERROR: missing FROM-clause entry for table "excluded"사용하면 그냥 나에게 준다 DO NOTHING.
Der Hochstapler

답변:


8

삽입 될 값이 모두 새 테이블이거나 이미 테이블 또는 믹스에있는 경우 3 가지 경우 모두에서 테스트가 완료됩니다.

WITH
  val (name) AS
    ( VALUES                          -- rows to be inserted
        ('foo'),
        ('bar'),
        ('zzz')
    ),
  ins AS
    ( INSERT INTO
        tag (name)
      SELECT name FROM val
      ON CONFLICT (name) DO NOTHING
      RETURNING id, name              -- only the inserted ones
    )
SELECT COALESCE(ins.id, tag.id) AS id, 
       val.name
FROM val
  LEFT JOIN ins ON ins.name = val.name
  LEFT JOIN tag ON tag.name = val.name ;

아마도 새로운 ON CONFLICT구문 을 사용하지 않고 다른 방법이있을 것 입니다.


4

이것이 어떻게 수행 될지 모르지만 시도 할 수있는 다른 옵션처럼, 여기에 똑같은 구식 방법이 있습니다 ( ON CONFLICT).

WITH items (name) AS (VALUES ('foo'), ('bar'), ('zzz')),
     added        AS
      (
        INSERT INTO tag (name)

        SELECT name FROM items
        EXCEPT
        SELECT name FROM tag

        RETURNING id
      )
SELECT id FROM added

UNION ALL

SELECT id FROM tag
WHERE name IN (SELECT name FROM items)
;

즉, tag테이블 에서 찾을 수없는 [고유 한] 이름 만 삽입 하고 ID를 리턴하십시오. tag최종 출력을 위해 이를에 존재하는 이름의 ID와 결합 하십시오. 또한 던질 수있는 name바와 같이, 출력에 ypercubeᵀᴹ 제안 당신이 어떤 이름 ID 일치를 알 수 있도록하는 것이.


1
예. 내 코드의 마지막 SELECT도 같이 쓸 수있다SELECT .. FROM ins UNION ALL SELECT ... FROM val JOIN tag ... ;
ypercubeᵀᴹ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.