ดัชนีที่ไม่ซ้ำที่เลื่อนออกไปใน postgres


14

เมื่อมองไปที่เอกสารประกอบของ postgres สำหรับตารางการเปลี่ยนแปลงดูเหมือนว่าข้อ จำกัด ทั่วไปสามารถทำเครื่องหมายเป็นDEFERRABLE(เพิ่มเติมอย่างชัดเจนINITIALLY DEFERREDซึ่งเป็นสิ่งที่ฉันสนใจ)

ดัชนียังสามารถเชื่อมโยงกับข้อ จำกัด ได้ตราบใดที่:

ดัชนีไม่สามารถมีคอลัมน์นิพจน์หรือดัชนีบางส่วนได้

ซึ่งทำให้ฉันเชื่อว่าขณะนี้ไม่มีวิธีที่จะมีดัชนีที่ไม่ซ้ำกับเงื่อนไขเช่น:

CREATE UNIQUE INDEX unique_booking
  ON public.booking
  USING btree
  (check_in, check_out)
  WHERE booking_status = 1;

จะINITIALLY DEFERREDหมายถึงว่า 'ข้อ จำกัด ' ที่ไม่ซ้ำกันจะได้รับการตรวจสอบในตอนท้ายของการทำธุรกรรม (ถ้าSET CONSTRAINTS ALL DEFERRED;ใช้)

การสันนิษฐานของฉันถูกต้องและถ้าเป็นเช่นนั้นมีวิธีใดบ้างที่จะบรรลุเป้าหมายที่ต้องการ?

ขอบคุณ

คำตอบ:


15

ไม่สามารถเลื่อนดัชนีได้ - ไม่สำคัญว่าจะเป็นUNIQUEเพียงบางส่วนหรือไม่มีเพียงUNIQUEข้อ จำกัด ประเภทอื่น ๆ จำกัด ( FOREIGN KEY, PRIMARY KEY, EXCLUDE) นอกจากนี้ยังมี deferrable - แต่ไม่CHECKจำกัด

ดังนั้นดัชนีบางส่วนที่ไม่ซ้ำกัน (และข้อ จำกัด โดยนัยที่ใช้) จะถูกตรวจสอบที่ทุกคำสั่ง (และในความเป็นจริงหลังจากที่ทุกแถวแทรก / ปรับปรุงในการใช้งานปัจจุบัน) ไม่ได้อยู่ในตอนท้ายของการทำธุรกรรม


สิ่งที่คุณสามารถทำได้ถ้าคุณต้องการใช้ข้อ จำกัด นี้ว่าเป็นการเลื่อนออกไปคือการเพิ่มตารางอีกหนึ่งตารางในการออกแบบ บางสิ่งเช่นนี้

CREATE TABLE public.booking_status
  ( booking_id int NOT NULL,               -- same types
    check_in timestamp NOT NULL,           -- as in  
    check_out timestamp NOT NULL,          -- booking
    CONSTRAINT unique_booking
        UNIQUE (check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED,
    CONSTRAINT unique_booking_fk
        FOREIGN KEY (booking_id, check_in, check_out)
        REFERENCES public.booking (booking_id, check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED
  ) ;

ด้วยการออกแบบนี้และสมมติว่าbooking_statusมีเพียง 2 ตัวเลือกที่เป็นไปได้ (0 และ 1) คุณสามารถลบได้ทั้งหมดbooking(ถ้ามีแถวที่booking_statusมันเป็น 1 ถ้าไม่ใช่ 0)


อีกวิธีหนึ่งคือ (ab) ใช้EXCLUDEข้อ จำกัด :

ALTER TABLE booking
    ADD CONSTRAINT unique_booking
        EXCLUDE 
          ( check_in  WITH =, 
            check_out WITH =, 
            (CASE WHEN booking_status = 1 THEN TRUE END) WITH =
          ) 
        DEFERRABLE INITIALLY DEFERRED ;

ทดสอบที่dbfiddle

สิ่งที่กล่าวมาข้างต้นทำ:

  • การCASEแสดงออกกลายเป็นNULLเมื่อbooking_statusเป็นโมฆะหรือแตกต่างจาก 1 เราสามารถเขียน(CASE WHEN booking_status = 1 THEN TRUE END)ราวกับ(booking_status = 1 OR NULL)ว่ามันทำให้ชัดเจนมากขึ้น

  • ข้อ จำกัด ที่ไม่ซ้ำกันและไม่รวมยอมรับแถวที่มีนิพจน์อย่างน้อยหนึ่งรายการเป็น NULL WHERE booking_status = 1ดังนั้นจะทำหน้าที่เป็นดัชนีกรองด้วย

  • ทั้งหมดWITHประกอบการ=จึงทำหน้าที่เป็นUNIQUEข้อ จำกัด

  • ทั้งสองรวมกันทำให้ข้อ จำกัด ทำหน้าที่เป็นดัชนีที่ไม่ซ้ำกันกรอง

  • แต่ข้อ จำกัด และEXCLUDEข้อ จำกัด สามารถเลื่อนได้


2
+1 สำหรับรุ่น EXCLUDE เป็นสิ่งที่ฉันต้องการ นี่เป็นอีกตัวอย่างที่แสดงความสามารถของEXCLUDE
Benjamin Peter

(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)ควรถูกแทนที่ด้วย) WHERE (booking_status = 1)เพราะ "ข้อ จำกัด การยกเว้นถูกนำมาใช้โดยใช้ดัชนี" และดัชนีบางส่วนนี้WHEREจะเล็กลงและเร็วขึ้น - postgresql.org/docs/current/sql-createtable.htmlและpostgresql.org/docs/current/sql- createindex.html
Denis Ryzhkov

1

แม้ว่าคำถามที่ผ่านมาหลายปีผ่านไปแล้ว แต่ฉันต้องการชี้แจงให้ผู้พูดภาษาสเปนได้ทำการทดสอบใน Postgres:

มีการเพิ่มข้อ จำกัด ต่อไปนี้ลงในตารางของระเบียน 1337 ซึ่งชุดเป็นคีย์หลัก:

**Bloque 1**
ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit) 

สิ่งนี้สร้างคีย์หลักเริ่มต้นที่ไม่ได้กำหนดไว้สำหรับตารางดังนั้นเมื่อพยายามอัปเดตครั้งต่อไปเราจะได้รับข้อผิดพลาด:

update ele_kitscompletos
set div_nkit = div_nkit + 1; 

ข้อผิดพลาด: คีย์ที่ซ้ำกันละเมิดข้อ จำกัด ที่ไม่ซ้ำกัน« unique_div_nkit »

ใน Postgres การดำเนินการ UPDATE สำหรับแต่ละ ROW จะตรวจสอบว่าเป็นไปตามข้อ จำกัด หรือข้อ จำกัด


ตอนนี้ CONSTRAINT IMMEDIATE ถูกสร้างขึ้นและแต่ละคำสั่งจะถูกดำเนินการแยกจากกัน:

ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY IMMEDIATE

**Bloque 2**
BEGIN;   
UPDATE ele_kitscompletos set div_nkit = div_nkit + 1;
INSERT INTO public.ele_kitscompletos(div_nkit, otro_campo)
VALUES 
  (1338, '888150502');
COMMIT;

ข้อความค้นหาตกลง, 0 แถวที่ได้รับผลกระทบ (เวลาดำเนินการ: 0 ms; เวลาทั้งหมด: 0 ms) แบบสอบถามตกลง, แถวที่ได้รับผลกระทบ 1328 แถว (เวลาดำเนินการ: 858 ms; เวลาทั้งหมด: 858 มิลลิวินาที) ข้อผิดพลาด: : Ya existe la llave (div_nkit) = (1338)

ที่นี่ SI อนุญาตให้เปลี่ยนคีย์หลักเนื่องจากเรียกใช้ประโยคที่สมบูรณ์ทั้งประโยคแรก (1328 แถว) แต่ถึงแม้ว่ามันจะอยู่ในทรานแซคชัน (BEGIN), CONSTRAINT จะถูกตรวจสอบทันทีเมื่อจบแต่ละประโยคโดยไม่ทำ COMMIT ดังนั้นจึงสร้างข้อผิดพลาดเมื่อดำเนินการ INSERT ในที่สุดเราก็สร้าง CONSTRAINT DEFERRED ทำต่อไปนี้:

**Bloque 3**
ALTER TABLE public.ele_edivipol
DROP CONSTRAINT unique_div_nkit RESTRICT;   

ALTER TABLE ele_edivipol
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY DEFERRED

หากเราดำเนินการแต่ละคำสั่งของ ** บล็อก 2 ** แต่ละประโยคแยกกันจะไม่มีการสร้างข้อผิดพลาดให้กับ INSERT เพราะมันไม่ได้ตรวจสอบ แต่คอมมิชชันขั้นสุดท้ายจะถูกดำเนินการเมื่อพบความไม่สอดคล้องกัน


สำหรับข้อมูลที่สมบูรณ์เป็นภาษาอังกฤษฉันขอแนะนำให้คุณตรวจสอบลิงก์:

ข้อ จำกัด SQL ที่เลื่อนออกไปในเชิงลึก

ไม่ผิดพลาดเมื่อเทียบกับ DEFERRABLE เริ่มต้นทันที

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.