Postgres : SET NOT NULL이 CHECK 제약 조건보다 어떻게 "더 효율적"입니까?


17

에서 제약 조건에 대한 PostgreSQL의 문서 , 그것은 말한다

null이 아닌 제약 조건은 기능적으로 check constraint를 만드는 것과 동일 CHECK (column_name IS NOT NULL)하지만 PostgreSQL에서는 명시적인 null이 아닌 제약 조건을 만드는 것이 더 효율적입니다.

궁금하네요

  • "보다 효율적인"이란 정확히 무엇을 의미합니까?
  • CHECK (column_name IS NOT NULL)대신에 사용하면 어떤 단점 이 SET NOT NULL있습니까?

NOT VALID CHECK제약 조건을 추가하고 별도로 검증 할 수 있기를 원합니다 (따라서 제약 조건을 추가 AccessExclusiveLock하기 위해 짧은 기간 동안 만 유지 한 다음 ShareUpdateExclusiveLock더 긴 유효성 검사 단계를 위해 a 를 유지합니다).

ALTER TABLE table_name
  ADD CONSTRAINT column_constraint
  CHECK (column_name IS NOT NULL)
  NOT VALID;
ALTER TABLE table_name
  VALIDATE CONSTRAINT column_constraint;

대신에:

ALTER TABLE table_name
  ALTER COLUMN column_name
  SET NOT NULL;


not in두 변형을 모두 사용 하면 실행 계획은 어떻게 생깁니 까? 그것들은 같습니까, 아니면 다릅니 까?
Martin Smith

답변:


12

내 생각에는 "더 효율적"은 "검사를 수행하는 데 시간이 덜 소요됨"을 의미합니다 (시간 우위). "검사를 수행하는 데 필요한 메모리가 적습니다"(공간 이점)를 의미 할 수도 있습니다. 또한 "부적절하지 않은 것"(예 : 무언가를 잠그지 않거나 짧은 시간 동안 잠그는 등)을 의미 할 수도 있지만 "추가 이점"을 알거나 확인할 방법이 없습니다.

가능한 공간 이점을 확인하는 쉬운 방법을 생각할 수 없습니다 (요즘에는 메모리가 저렴할 때 그렇게 중요하지 않은 것 같습니다). 반면에 가능한 시간 이점을 확인하는 것은 어렵지 않습니다. 제약 조건을 제외하고는 동일한 두 개의 테이블 만 작성하십시오. 충분히 많은 수의 행을 삽입하고 몇 번 반복 한 후 타이밍을 확인하십시오.

이것은 테이블 설정입니다.

CREATE TABLE t1
(
   id serial PRIMARY KEY, 
   value integer NOT NULL
) ;

CREATE TABLE t2
(
  id serial PRIMARY KEY,
  value integer
) ;

ALTER TABLE t2
  ADD CONSTRAINT explicit_check_not_null
  CHECK (value IS NOT NULL);

타이밍을 저장하는 데 사용되는 추가 테이블입니다.

CREATE TABLE timings
(
   test_number integer, 
   table_tested integer /* 1 or 2 */, 
   start_time timestamp without time zone,
   end_time timestamp without time zone,
   PRIMARY KEY(test_number, table_tested)
) ;

그리고 이것은 pgAdmin III 및 pgScript 기능을 사용하여 수행 된 테스트 입니다.

declare @trial_number;
set @trial_number = 0;

BEGIN TRANSACTION;
while @trial_number <= 100
begin
    -- TEST FOR TABLE t1
    -- Insert start time
    INSERT INTO timings(test_number, table_tested, start_time) 
    VALUES (@trial_number, 1, clock_timestamp());

    -- Do the trial
    INSERT INTO t1(value) 
    SELECT 1.0
      FROM generate_series(1, 200000) ;

    -- Insert end time
    UPDATE timings 
       SET end_time=clock_timestamp() 
     WHERE test_number=@trial_number and table_tested = 1;

    -- TEST FOR TABLE t2
    -- Insert start time
    INSERT INTO timings(test_number, table_tested, start_time) 
    VALUES (@trial_number, 2, clock_timestamp());

        -- Do the trial
    INSERT INTO t2(value) 
    SELECT 1.0
    FROM generate_series(1, 200000) ;

    -- Insert end time
    UPDATE timings 
       SET end_time=clock_timestamp() 
     WHERE test_number=@trial_number and table_tested = 2;

    -- Increase loop counter
    set @trial_number = @trial_number + 1;
end 
COMMIT TRANSACTION;

결과는 다음 쿼리에 요약되어 있습니다.

SELECT
    table_tested, 
    sum(delta_time), 
    avg(delta_time), 
    min(delta_time), 
    max(delta_time), 
    stddev_pop(delta_time) 
FROM
    (
    SELECT
        table_tested, extract(epoch from (end_time - start_time)) AS delta_time
    FROM
        timings
    ) AS delta_times
GROUP BY
    table_tested 
ORDER BY
    table_tested ;

다음과 같은 결과가 나타납니다.

table_tested | sum     | min   | max   | avg   | stddev_pop
-------------+---------+-------+-------+-------+-----------
           1 | 176.740 | 1.592 | 2.280 | 1.767 | 0.08913
           2 | 177.548 | 1.593 | 2.289 | 1.775 | 0.09159

값의 그래프는 중요한 변동성을 보여줍니다.

각 200000 개의 행 삽입에 소요 된 시간 (초)

따라서 실제로 CHECK (열이 NULL이 아님)는 매우 약간 느립니다 (0.5 %). 그러나 타이밍의 변동성이 그보다 훨씬 큰 경우,이 작은 차이는 임의의 이유 때문일 수 있습니다. 따라서 통계적으로 중요하지 않습니다.

실제적인 관점에서, 나는 그것이 "더 효율적"이라는 것을 무시할 것입니다 NOT NULL. 왜냐하면 그것이 실제로 중요하지 않다고 생각하기 때문입니다. 반면 나는의 부재 AccessExclusiveLock가 장점 이라고 생각합니다 .

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