PostgreSQL에 행이 존재하는지 확인


177

테이블에 삽입 해야하는 많은 행이 있지만 이러한 삽입은 항상 일괄 처리로 수행됩니다. 그래서 배치의 단일 행이 테이블에 존재하는지 확인하고 싶습니다.

따라서 기본 키 검사는 아니지만 너무 중요하지는 않습니다. 단일 행만 확인하고 싶을 count(*)수도 있으므로 아마 좋지 않습니다 exists.

그러나 PostgreSQL을 처음 접했기 때문에 알고있는 사람들에게 물어보십시오.

내 배치에는 다음 구조의 행이 있습니다.

userid | rightid | remaining_count

따라서 테이블에 제공된 행이 있으면 모두 행이 userid있음을 의미합니다.


테이블에 행이 있는지 또는 배치의 행이 있는지 확인하고 싶습니까?
JNK

내 배치의 모든 행 예. 그들은 모두 같은 필드를 약간 편집하여 공유합니다.
Valentin Kuzub

질문을 명확히하십시오. 일괄 적으로 레코드를 추가하고 싶습니까? 카운트에 특별한 것이 있습니까? (BTW 예약어, 열 이름으로는 실용적이지
않음

좋아, 실제 상황을 약간 단순화하려고했지만 실제 구현에 점점 더 가까워지고 있습니다. 해당 행이 삽입되면 (다른 필드 for_date 필드가 있음) 특정 권한을 사용할 때 지정된 사용자에 대한 권한이 감소하기 시작합니다. 권한이 0이되면 해당 날짜에 대해 더 이상 해당 작업을 수행 할 수 없습니다.
that 's

1
테이블 정의를 표시하고 (관련 부분) 수행하려는 작업을 알려주십시오.
wildplasser

답변:


345

TRUE / FALSE 리턴에 EXISTS 키워드를 사용하십시오.

select exists(select 1 from contact where id=12)

21
이것에 대한 확장은 쉽게 참조 할 수 있도록 반환 된 열의 이름을 지정할 수 있습니다. 예 :select exists(select 1 from contact where id=12) AS "exists"
Rowan

3
이는 프로그래밍 언어에 따라 때때로 None 대신 항상 값 (true 또는 false)을 반환하므로 예상대로 확장되지 않을 수 있으므로 더 좋습니다.
isaaclw

1
이 방법을 사용하여 Seq Scan을 가지고 있습니다. 내가 뭔가 잘못 했어?
FiftiN

2
@ Michael.MI에는 3 천만 행의 DB 테이블이 있으며 Postgres가 인덱스 스캔 대신 Seq Scan을 사용하기 때문에 사용 exists하거나 limit 1성능이 크게 저하됩니다. 그리고 analyze도움이되지 않습니다.
FiftiN

2
@maciek 그 'ID가'해당 ID를 가진 하나 개의 레코드 만 있기 때문에 그래서 "LIMIT 1"무의미, 기본 키입니다 양해 해 주시기 바랍니다
StartupGuy

34

어떻습니까?

select 1 from tbl where userid = 123 limit 1;

123삽입하려는 배치의 사용자 ID는 어디에 있습니까 ?

위의 쿼리는 주어진 사용자 ID를 가진 레코드가 있는지에 따라 빈 세트 또는 단일 행을 반환합니다.

이 속도가 너무 느리면에 색인을 생성 할 수 tbl.userid있습니다.

배치의 단일 행조차도 테이블에 존재하면이 경우 행이 모두 삽입되었는지 확인하기 때문에 행을 삽입 할 필요가 없습니다.

프로그램이 배치 중간에 중단 되더라도이 상태를 유지하려면 데이터베이스 트랜잭션을 적절하게 관리해야합니다 (예 : 전체 배치가 단일 트랜잭션 내에 삽입 됨).


11
항상 count (*) 값이 0 또는 1 인 행을 반환하도록 보장되므로 프로그래밍 방식으로 "(select 1 ... limit 1)에서 count (*) 선택"이 더 쉬울 수 있습니다.
David Aldridge

@DavidAldridge count (*)는 여전히 모든 행을 읽어야한다는 것을 의미하지만, 제한 1은 첫 번째 레코드에서 중지하고 다음을 반환합니다.
Imraan

3
@Imraan 나는 당신이 쿼리를 잘못 해석했다고 생각합니다. 는 COUNT중첩에 작용 SELECT((가) 때문에 기껏해야 1 개 행을 갖는다 LIMIT부질이다).
jpmc26

9
INSERT INTO target( userid, rightid, count )
  SELECT userid, rightid, count 
  FROM batch
  WHERE NOT EXISTS (
    SELECT * FROM target t2, batch b2
    WHERE t2.userid = b2.userid
    -- ... other keyfields ...
    )       
    ;

BTW : 중복으로 인해 전체 배치가 실패하려면 (기본 키 제약 조건이 부여됨)

INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count 
FROM batch
    ;

당신이 원하는 것을 정확하게 할 것입니다 : 성공하거나 실패합니다.


각 행을 확인합니다. 그는 한 번의 점검을 원합니다.
JNK

1
아니요, 한 번만 확인합니다. 하위 쿼리는 상관이 없습니다. 일치하는 쌍이 발견되면 구제됩니다.
wildplasser

맞아요, 나는 그것이 외부 쿼리를 언급했다고 생각했습니다. +1
JNK

BTW : 쿼리가 트랜잭션 내부에 있으므로 중복 ID를 삽입하면 아무 일도 일어나지 않으므로 하위 쿼리를 생략 할 수 있습니다.
wildplasser

흠 나는 확실하지 않다. 권한이 삽입되면 카운트 열이 감소하기 시작합니다. (그림에 대한 일부 세부 정보 만) 행이 이미 존재하고 하위 쿼리가 생략되면 중복 된 고유 키가 던져 질 때 오류가 발생한다고 생각합니까? (독특한 키의 사용자 ID 형식)
Valentin Kuzub

1
select true from tablename where condition limit 1;

이것이 postgres가 외래 키를 확인하는 데 사용하는 쿼리라고 생각합니다.

귀하의 경우에는 한 번 에이 작업을 수행 할 수도 있습니다.

insert into yourtable select $userid, $rightid, $count where not (select true from yourtable where userid = $userid limit 1);

1

@MikeM이 지적했듯이.

select exists(select 1 from contact where id=12)

인덱스 접촉에, 보통 1 MS에 시간 비용을 줄일 수 있습니다.

CREATE INDEX index_contact on contact(id);

0
SELECT 1 FROM user_right where userid = ? LIMIT 1

결과 집합에 행이 포함되어 있으면 삽입 할 필요가 없습니다. 그렇지 않으면 기록을 삽입하십시오.


묶음에 100 개의 행이 포함되어 있으면 100 개의 행이 반환됩니다.
Valentin Kuzub

한 행으로 제한 할 수 있습니다. 더 잘 수행해야합니다. @aix의 편집 된 답변을 살펴보십시오.
Fabian Barney

0

performace에 대해 생각하면 다음과 같이 함수에서 "PERFORM"을 사용할 수 있습니다.

 PERFORM 1 FROM skytf.test_2 WHERE id=i LIMIT 1;
  IF FOUND THEN
      RAISE NOTICE ' found record id=%', i;  
  ELSE
      RAISE NOTICE ' not found record id=%', i;  
 END IF;

나와 함께 작동하지 않습니다 : 수행 근처에서 구문 오류가 발생합니다
Simon

1
이는 SQL이 아닌 pl / pgsql이므로 SQL로 실행하려고하면 "PERFORM"에 대한 구문 오류가 발생합니다
Mark K Cowan

-1

내가 특별히 당신의 문장을 해결하기 위해 다른 생각을 제안하고 싶습니다 : "나는 때문에 배치에서 하나의 행이 테이블에 존재하는지 확인하려는 그래서 나는 그들이 모두 알고 다음 되었다 삽입 ."

당신은 "배치"에 삽입하여 일을 효율적으로 만들고 있지만 존재 확인을 한 번에 한 레코드 씩 수행합니까? 이것은 나에게 직관적이지 않습니다. 당신이 말할 때 그래서 " 삽입은 항상 일괄 적으로 수행하는 " 당신은 당신이 하나의 INSERT 문으로 여러 레코드를 삽입하는 의미 나는 그것을 가지고 . Postgres가 ACID를 준수한다는 것을 알아야합니다. 하나의 insert 문으로 여러 레코드 (일괄 데이터) 를 삽입하는 경우 일부 레코드가 삽입 되었는지 여부를 확인할 필요가 없습니다. 명령문이 통과되거나 실패합니다. 모든 레코드가 삽입되거나 없습니다.

반면에 C # 코드가 루프와 같이 단순히 "set"별도의 insert 문을 수행하는 경우 이는 "batch"입니다. 실제로 "#" 인서트는 항상 배치로 수행됩니다. " "일괄 처리"라고 부르는 부분이 실제로 삽입되지 않을 수 있으므로 실제로 검사 할 필요가 있다고 생각하면 이것이 더 근본적인 문제가있는 것입니다. 실제로 하나의 삽입으로 여러 레코드를 삽입하고 개별 레코드가 작성했는지 확인하지 않으려면 패러다임을 변경해야합니다.

이 예제를 고려하십시오.

CREATE TABLE temp_test (
    id SERIAL PRIMARY KEY,
    sometext TEXT,
    userid INT,
    somethingtomakeitfail INT unique
)
-- insert a batch of 3 rows
;;
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 1, 1),
('bar', 2, 2),
('baz', 3, 3)
;;
-- inspect the data of what we inserted
SELECT * FROM temp_test
;;
-- this entire statement will fail .. no need to check which one made it
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 2, 4),
('bar', 2, 5),
('baz', 3, 3)  -- <<--(deliberately simulate a failure)
;;
-- check it ... everything is the same from the last successful insert ..
-- no need to check which records from the 2nd insert may have made it in
SELECT * FROM temp_test

이것은 실제로 Postgresql뿐만 아니라 모든 ACID 호환 DB에 대한 패러다임입니다. 다시 말해, "일괄 처리"개념을 수정하고 우선 행 단위 검사를 수행하지 않는 것이 좋습니다.

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