2010-01-10 2 views
6

PostgreSQL 데이터베이스에서 고유 키가 필요한 테이블과 기본 키가있는 테이블이 있습니다.거기에 PostgreSQL에서 삽입이 실패하면 nextval() 호출을 피할 수있는 방법이 있습니까?

CREATE TABLE users (
    id  INTEGER PRIMARY KEY DEFAULT nextval('groups_id_seq'::regclass), 
    name VARCHAR(255) UNIQUE NOT NULL 
); 

    INSERT users (name) VALUES ('foo'); 
    INSERT users (name) VALUES ('foo'); 
    INSERT users (name) VALUES ('bar'); 

번째 입력이 실패하지만 ID 번호에 갭을 남긴다 첨가 '바'때 시퀀스 groups_id_seq는 이미 증가된다.

다른 제약 조건이 충족되는 경우에만 PostgreSQL에 다음 값을 가져 오도록 지시하는 방법이 있습니까? 아니면 이름이 중복되지 않으면 SELECT를 사용하여 먼저 확인해야합니까? 이것은 여전히 ​​간격의 부족을 보장하지는 않지만 적어도 동시에 동일한 이름을 삽입하려고하는 다른 프로세스가있을 때 드문 경우로 번호를 줄입니다

+0

참조 : http://stackoverflow.com/a/9985219/398670 –

+0

가능한 [SQL 테이블의 기본 키 ID 사이의 간격] (http://stackoverflow.com/questions/39099905/gaps-between- primary-key-id-in-sql-table) – e4c5

답변

12

나는 그렇게 생각하지 않습니다 : 기본 기능 (ROLLBACK을 수행하는 두 개의 동시 트랜잭션을 생각해보십시오.) 틈을 무시해야합니다. 왜 그들은 당신의 경우에 문제가됩니까?

+0

정말로 문제는 아니지만, 나는 그것을보고 놀랐으며, 대부분 피할 수 있다면 어떻게 에너지를 투자해서 어떻게 배울 것인가? 나는 그것을 100 % 피할 수 없다는 것을 안다. – szabgab

+5

아니요, 피할 수 없습니다. 시퀀스에서 꽤 큰 차이를 유발할 수있는 다른 경우도 있습니다. 시퀀스가 ​​증가하는 것을 보장한다는 점 (데이터 유형이 32 또는 64 비트 일 때까지 최대 값을 유지할 수있을 때까지)을 보장하지만, gapless. –

+1

@Magnus : 그들은 피할 수 있지만 복잡성면에서 큰 비용이 듭니다. 가능한 경우 격차를 허용하도록 설계하는 것이 가장 좋습니다. –

6

gapless 시퀀스가 ​​필요한 경우 - 할 수있는 방법이 있지만 사소하지는 않지만 훨씬 느립니다.

- "너무 많은 ID 사용"에 대해 걱정하는 경우 - id를 bigserial로 정의하십시오.

5

이 작업을 수행하는 것이 번거롭긴하지만 가능합니다. bortzmeyer says처럼 연속되는 시퀀스의 값에 의존하는 것은 위험하므로 가능한 경우 그대로 두는 것이 가장 좋습니다.

경우 당신은 할 수 없습니다 :

행 (즉, 해당 테이블에 대한 모든 INSERT이며, 특정 이름이 발생할 수있는 테이블에 대한 모든 액세스 권한을 (를 허용하는 경우가 가난한 연습 비록) name 필드를 변경할 수있는 모든 UPDATE)은 먼저을 잠그는 트랜잭션 내에서 을 수행해야합니다. 가장 간단하고 성능이 가장 낮은 옵션은 LOCK users IN EXCLUSIVE MODE을 사용하여 전체 테이블을 잠그는 것입니다 (마지막 세 단어를 추가하면 다른 프로세스에서 동시 읽기 액세스가 허용되므로 안전합니다).

그러나 이것은 많은 동시 수정이 users 인 경우 성능을 저하시키는 매우 거친 잠금입니다. 더 좋은 옵션은 이미 존재해야하는 다른 테이블의 단일 행을 잠그는 것입니다. 이 행은 SELECT ... FOR UPDATE으로 잠글 수 있습니다. 이는 다른 "상위"테이블에 대한 FK 종속성이있는 "하위"테이블로 작업 할 때만 의미가 있습니다.

예를 들어, 실제로는 customer에 대해 새로운 orders을 안전하게 만들고이 주문에 '이름'을 식별하는 것이 있다고 상상해보십시오. (나도 알아, 가난한 예를 ...) orderscustomers에 대한 FK 종속성을가집니다. 그런 다음 해당 고객에 대해 동일한 이름을 가진 두 개의 주문을 만드는 이제까지 방지하기 위해, 다음을 수행 할 수 : 그것은이다

BEGIN; 

-- Customer 'jbloggs' must exist for this to work. 
SELECT 1 FROM customers 
WHERE id = 'jbloggs' 
FOR UPDATE 

-- Provided every attempt to create an order performs the above step first, 
-- at this point, we will have exclusive access to all orders for jbloggs. 
SELECT 1 FROM orders 
WHERE id = 'jbloggs' 
AND order_name = 'foo' 

-- Determine if the preceding query returned a row or not. 
-- If it did not: 
INSERT orders (id, name) VALUES ('jbloggs', 'foo'); 

-- Regardless, end the transaction: 
END; 

참고 하지 단순히 SELECT ... FOR UPDATEusers에서 해당 행을 고정하기에 충분 - 경우 행이 아직 존재하지 않는 경우, 여러 동시 프로세스가 행이 존재하지 않는다고 동시에보고 한 다음 동시 삽입을 시도하여 실패한 트랜잭션 및 순서 갭을 초래할 수 있습니다.

잠금 체계가 작동합니다. 중요한 점은 같은 이름을 가진 행을 만들려고하는 사람은 같은 객체를 잠글려고합니다..

관련 문제