Postgres: Làm thế nào để SET KHÔNG NULL và hiệu quả hơn so với ràng buộc CHECK


16

Trong các tài liệu PostgreSQL cho các ràng buộc , nó nói

Một ràng buộc không null tương đương về mặt chức năng với việc tạo ra một ràng buộc kiểm tra CHECK (column_name IS NOT NULL), nhưng trong PostgreQuery, việc tạo một ràng buộc không null rõ ràng sẽ hiệu quả hơn.

tôi tự hỏi

  • Chính xác thì nó có nghĩa là gì "hiệu quả hơn"?
  • Những bất lợi của việc sử dụng CHECK (column_name IS NOT NULL)thay vì là SET NOT NULLgì?

Tôi muốn có thể thêm một NOT VALID CHECKràng buộc và xác thực nó một cách riêng biệt (vì vậy AccessExclusiveLocknó chỉ được giữ trong một khoảng thời gian ngắn để thêm ràng buộc và sau đó a ShareUpdateExclusiveLockđược giữ cho bước xác thực dài hơn):

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

Thay vì:

ALTER TABLE table_name
  ALTER COLUMN column_name
  SET NOT NULL;


Kế hoạch thực hiện trông như thế nào nếu bạn thực hiện not invới cả hai biến thể? Chúng giống nhau hay chúng khác nhau?
Martin Smith

Câu trả lời:


11

Dự đoán hoang dã của tôi: "hiệu quả hơn" có nghĩa là "cần ít thời gian hơn để thực hiện kiểm tra" (lợi thế về thời gian). Điều này cũng có nghĩa là "cần ít bộ nhớ hơn để thực hiện kiểm tra" (lợi thế về không gian). Điều này cũng có nghĩa là "có ít tác dụng phụ hơn" (chẳng hạn như không khóa hoặc khóa trong thời gian ngắn hơn) ... nhưng tôi không có cách nào để biết hoặc kiểm tra "lợi thế bổ sung" đó.

Tôi không thể nghĩ ra một cách dễ dàng để kiểm tra lợi thế không gian có thể có (mà tôi đoán là không quan trọng khi bộ nhớ ngày nay rẻ). Mặt khác, không khó để kiểm tra lợi thế về thời gian có thể: chỉ cần tạo hai bảng giống nhau, ngoại trừ ràng buộc duy nhất. Chèn một số lượng hàng đủ lớn, lặp lại một vài lần và kiểm tra thời gian.

Đây là thiết lập bảng:

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);

Đây là một bảng phụ, được sử dụng để lưu trữ thời gian:

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)
) ;

Và đây là thử nghiệm được thực hiện, sử dụng pgAdmin III và tính năng 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;

Kết quả được tóm tắt trong truy vấn sau:

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 ;

Với kết quả như sau:

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

Một biểu đồ của các giá trị cho thấy một sự thay đổi quan trọng:

Thời gian dành cho mỗi lần chèn 200000 hàng (tính bằng giây)

Vì vậy, trong thực tế, KIỂM TRA (cột IS KHÔNG NULL) chậm hơn một chút (bằng 0,5%). Tuy nhiên, sự khác biệt nhỏ này có thể là do bất kỳ lý do ngẫu nhiên nào, với điều kiện là độ biến thiên của thời gian lớn hơn nhiều. Vì vậy, nó không có ý nghĩa thống kê.

Từ quan điểm thực tế, tôi sẽ rất bỏ qua "hiệu quả hơn" NOT NULL, bởi vì tôi không thực sự thấy nó có ý nghĩa; trong khi tôi nghĩ rằng sự vắng mặt của một AccessExclusiveLocklà một lợi thế.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.