คอลัมน์อาร์เรย์ดัชนี PostgreSQL สามารถได้หรือไม่


144

ฉันไม่พบคำตอบที่ชัดเจนสำหรับคำถามนี้ในเอกสารประกอบ หากคอลัมน์เป็นประเภทอาร์เรย์ค่าที่ป้อนทั้งหมดจะถูกทำดัชนีแยกกันหรือไม่?

ฉันสร้างตารางง่าย ๆ ที่มีหนึ่งint[]คอลัมน์และวางดัชนีเฉพาะลงไป ฉันสังเกตเห็นว่าฉันไม่สามารถเพิ่มอาร์เรย์เหมือนกันได้ซึ่งทำให้ฉันเชื่อว่าดัชนีนั้นเป็นส่วนประกอบของรายการอาร์เรย์ไม่ใช่ดัชนีของแต่ละรายการ

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

ดัชนีนี้ช่วยค้นหาหรือไม่


เป็นไปได้ไหมที่จะใช้ประเภทข้อมูลjsonbและใช้ดัชนี? postgresql.org/docs/9.5/static/functions-json.htmlและpostgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING
user3791372

คำตอบ:


181

ใช่คุณสามารถดัชนีอาร์เรย์ แต่คุณต้องใช้ประกอบการอาร์เรย์และประเภท GIN-ดัชนี

ตัวอย่าง:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

ผลลัพธ์:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
บันทึก

ปรากฏว่าในหลายกรณีจำเป็นต้องใช้ตัวเลือกgin__int_ops

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

ฉันยังไม่เห็นกรณีที่มันจะทำงานกับตัวดำเนินการ && และ @> โดยไม่มีตัวเลือก gin__int_ops


19
ในฐานะที่เป็น OP คาดการณ์สิ่งนี้ไม่ได้จัดทำดัชนีค่าอาร์เรย์แต่ละค่า แต่จะทำดัชนีอาร์เรย์ทั้งหมดแทน ดังนั้นแม้ว่าสิ่งนี้จะช่วยให้แบบสอบถามมีปัญหา (ดูอธิบายแผน) ซึ่งหมายความว่าคุณไม่สามารถสร้างข้อ จำกัด ที่ไม่ซ้ำกัน (ง่าย) ในแต่ละค่าอาร์เรย์ ที่กล่าวว่าหากคุณใช้อาร์เรย์จำนวนเต็มคุณสามารถใช้โมดูล contrib "intarray" เพื่อทำดัชนีค่าอาร์เรย์แต่ละค่าซึ่งอาจเร็วกว่ามากในหลาย ๆ กรณี (IIRC มีงานบางอย่างที่ทำในเรื่องนี้สำหรับค่าข้อความ แต่ผู้มีส่วนร่วมอาจยินดีที่จะช่วยให้เสร็จ)
xzilla

15
โปรดอย่าใช้ตัวอักษรตัวพิมพ์ใหญ่ในตัวระบุ PostgreSQL ในตัวอย่างของรหัสมันแค่สับสนคนที่ไม่คุ้นเคยกับกฎการพับ / การอ้างอิงกรณีโดยเฉพาะอย่างยิ่งผู้ที่เพิ่งเริ่มใช้ PostgreSQL
intgr

6
หากต้องการแสดงความคิดเห็นของฉันซ้ำที่นี่: จากประสบการณ์ของฉันดัชนีเหล่านี้มีการเร่งความเร็วเพียงเล็กน้อยถึงไม่มีเลยเว้นแต่ gin__int_opsจะใช้สำหรับinteger[]คอลัมน์ ฉันใช้เวลาหลายปีในการหงุดหงิดและค้นหาวิธีแก้ไขปัญหาอื่น ๆ จนกว่าฉันจะค้นพบคลาส op มันเป็นผู้ปฏิบัติงานมหัศจรรย์แนวเขต
IamIC

1
@IAMIC หมายความว่าฉันไม่ควรรำคาญที่จะทำดัชนีอาร์เรย์ของสตริงหรือไม่? และฉันควรทำดัชนีอาร์เรย์จำนวนเต็มเท่านั้นหรือ
ryan2johnson9

93

@Tregoreg ตั้งคำถามในความคิดเห็นเพื่อรับรางวัลของเขา:

ฉันไม่พบคำตอบปัจจุบันที่ใช้งานได้ การใช้ดัชนี GIN ในคอลัมน์ที่พิมพ์อาร์เรย์จะไม่เพิ่มประสิทธิภาพของตัวดำเนินการใด ๆ () ไม่มีวิธีแก้ปัญหาจริงเหรอ?

คำตอบที่ยอมรับของ @ Frankบอกให้คุณใช้โอเปอเรเตอร์อาเรย์ซึ่งยังคงถูกต้องสำหรับ Postgres 11 คู่มือ:

... การกระจายมาตรฐานของ PostgreSQL รวมคลาส GIN โอเปอเรเตอร์สำหรับอาร์เรย์ซึ่งรองรับเคียวรีที่จัดทำดัชนีโดยใช้โอเปอเรเตอร์เหล่านี้:

<@
@>
=
&&

รายการที่สมบูรณ์ของคลาสตัวดำเนินการในตัวสำหรับดัชนี GIN ในการแจกแจงมาตรฐานอยู่ที่นี่

ในดัชนี Postgres ถูกผูกไว้กับโอเปอเรเตอร์ (ซึ่งนำไปใช้กับบางประเภท) ไม่ใช่ชนิดข้อมูลเพียงอย่างเดียวหรือฟังก์ชันหรืออย่างอื่น นั่นเป็นมรดกจากการออกแบบของ Berkeley ดั้งเดิมของ Postgresและยากมากที่จะเปลี่ยนแปลงในขณะนี้ และโดยทั่วไปก็ใช้งานได้ดี นี่คือกระทู้ใน pgsql-bug ที่ Tom Lane แสดงความคิดเห็นในเรื่องนี้

ฟังก์ชั่น PostGis บางอย่าง(เหมือนST_DWithin()) ดูเหมือนจะละเมิดหลักการนี้ แต่ก็ไม่เป็นเช่นนั้น ฟังก์ชั่นเหล่านั้นจะถูกเขียนใหม่ภายในเพื่อใช้ตัวดำเนินการที่เกี่ยวข้อง

นิพจน์ที่จัดทำดัชนีจะต้องอยู่ทางด้านซ้ายของตัวดำเนินการ สำหรับตัวดำเนินการส่วนใหญ่ ( รวมถึงทั้งหมดข้างต้น ) ตัววางแผนคิวรีสามารถทำได้โดยพลิกตัวถูกดำเนินการถ้าคุณวางนิพจน์ที่จัดทำดัชนีไว้ทางด้านขวา - เนื่องจากCOMMUTATORมีการกำหนด ANYสร้างสามารถนำมาใช้ในการทำงานร่วมกับผู้ประกอบการต่าง ๆ และไม่ได้เป็นผู้ประกอบการเอง เมื่อใช้เป็นconstant = ANY (array_expression)ดัชนีเฉพาะการสนับสนุน=ผู้ประกอบการในองค์ประกอบมากมาย= ANY()จะมีคุณสมบัติและเราจะต้องสับเปลี่ยนหา ดัชนี GIN หมดแล้ว

ปัจจุบัน Postgres ไม่ฉลาดพอที่จะรับนิพจน์ที่สามารถจัดทำดัชนี GIN ได้ สำหรับการเริ่มconstant = ANY (array_expression)เป็นไม่สมบูรณ์เทียบเท่าarray_expression @> ARRAY[constant]การ ตัวดำเนินการ Array ส่งคืนข้อผิดพลาดหากองค์ประกอบ NULL ใด ๆที่เกี่ยวข้องในขณะที่การANYสร้างสามารถจัดการกับ NULL ทั้งสองด้าน และมีผลลัพธ์ที่แตกต่างกันสำหรับชนิดข้อมูลไม่ตรงกัน

คำตอบที่เกี่ยวข้อง:

มานี

ในขณะที่ทำงานกับintegerอาร์เรย์ ( int4ไม่ใช่int2หรือint8) โดยไม่มีNULLค่า (เช่นตัวอย่างของคุณบอกเป็นนัย) ให้พิจารณาโมดูลเพิ่มเติมintarrayซึ่งให้บริการตัวดำเนินการพิเศษและการสนับสนุนดัชนีที่เร็วขึ้น ดู:

สำหรับUNIQUEข้อ จำกัด ในคำถามของคุณที่ยังไม่ได้รับคำตอบ: นั่นนำมาใช้กับดัชนี btree ในค่าอาร์เรย์ทั้งหมด (เช่นคุณสงสัย) และไม่ช่วยในการค้นหาองค์ประกอบเลย รายละเอียด:


1
Aaaaaaah ตอนนี้รู้สึกเขินอายมาก แต่มันก็ไม่ได้อยู่ในใจฉันว่า postgres จะไม่ใช้ดัชนีแม้ว่าจะเป็นไปได้ในทางทฤษฎี อาจเป็นเพราะฉันไม่มีความเข้าใจใน postgres เช่นดัชนีนั้นผูกพันกับตัวดำเนินการ ขอบคุณที่สละเวลาตอบคำถามที่ไม่ดีของฉันและแบ่งปันความรู้ของคุณ!
Tregoreg

6
@Tregoreg: อย่าอายเกินไปมันไม่ชัดเจนเกินไป ฉันจำได้ว่าสับสนด้วยตัวเองเมื่อฉันพบมันครั้งแรก คำถามเพิ่มเติมและการชี้แจงควรเป็นประโยชน์กับประชาชนทั่วไป
Erwin Brandstetter

1
จากประสบการณ์ของฉันดัชนีเหล่านี้ให้การเร่งความเร็วเพียงเล็กน้อยถึงไม่มีเลยเว้นแต่ gin__int_opsจะใช้สำหรับinteger[]คอลัมน์ ฉันใช้เวลาหลายปีในการหงุดหงิดและค้นหาวิธีแก้ไขปัญหาอื่น ๆ จนกว่าฉันจะค้นพบคลาส op มันเป็นผู้ปฏิบัติงานมหัศจรรย์แนวเขต
IamIC

2
@IAMIC: ฉันเพิ่มพอยน์เตอร์ไปยังอินทราเรย์ ดูเหมือนว่าน่าสังเกตในขณะที่คุณชี้ให้เห็น
Erwin Brandstetter

สำหรับANY (array_expression) = constantนิพจน์ GIN ดัชนีทำงานได้ดีหรือไม่?
user10375

37

ตอนนี้เป็นไปได้ที่จะจัดทำดัชนีองค์ประกอบอาเรย์แต่ละรายการ ตัวอย่างเช่น:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

สิ่งนี้ใช้ได้กับ Postgres อย่างน้อย 9.2.1 โปรดทราบว่าคุณต้องสร้างดัชนีแยกต่างหากสำหรับแต่ละดัชนีอาเรย์ในตัวอย่างของฉันฉันเพียงจัดทำดัชนีองค์ประกอบแรก


28
อย่าให้มันหายไป - วิธีการนี้ไม่มีความหมายสำหรับอาร์เรย์ความยาวผันแปรที่คุณต้องการใช้โอเปอเรเตอร์ ANY ()
Καrτhικ

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