Bisakah PostgreSQL kolom array indeks?

144

Saya tidak dapat menemukan jawaban yang pasti untuk pertanyaan ini dalam dokumentasi. Jika kolom adalah tipe array, apakah semua nilai yang dimasukkan akan diindeks secara individual?

Saya membuat tabel sederhana dengan satu int[]kolom, dan meletakkan indeks unik di atasnya. Saya perhatikan bahwa saya tidak dapat menambahkan array int yang sama, yang membuat saya percaya bahwa indeks adalah gabungan dari item array, bukan indeks dari setiap item.

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

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

Apakah indeks membantu permintaan ini?

IAMIC
sumber
Apakah mungkin untuk menggunakan tipe data jsonbdan menggunakan indeks? postgresql.org/docs/9.5/static/functions-json.html dan postgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING
user3791372

Jawaban:

181

Ya, Anda dapat mengindeks array, tetapi Anda harus menggunakan operator array dan tipe indeks GIN .

Contoh:

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

Hasil:

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
Catatan

tampaknya dalam banyak kasus opsi gin__int_ops diperlukan

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

Saya belum melihat kasus di mana ia akan bekerja dengan operator && dan @> tanpa opsi gin__int_ops

Frank Heikens
sumber
19
Sebagai OP menduga, ini sebenarnya tidak mengindeks nilai-nilai array individu, tetapi indeks keseluruhan array. Jadi, sementara ini akan membantu kueri yang dimaksud (lihat jelaskan rencana), ini berarti Anda tidak dapat membuat batasan unik (dengan mudah) pada nilai array individual. Yang mengatakan, jika Anda menggunakan array integer, Anda dapat menggunakan modul contrib "intarray" untuk mengindeks nilai-nilai array individual, yang bisa jauh lebih cepat dalam banyak kasus. (IIRC ada beberapa pekerjaan yang dilakukan untuk nilai teks ini, tetapi kontributor mungkin akan disambut untuk membantu menyelesaikannya).
xzilla
15
Tolong jangan gunakan huruf besar dalam pengidentifikasi PostgreSQL dalam contoh kode, itu hanya membingungkan orang-orang yang tidak terbiasa dengan aturan peliputan / pelipatan huruf, terutama orang-orang yang baru mengenal PostgreSQL.
Intgr
6
Untuk mengulangi komentar saya di sini: dari pengalaman saya, indeks ini menawarkan sedikit atau tidak ada percepatan kecuali gin__int_ops digunakan untuk integer[]kolom. Butuh bertahun-tahun frustrasi dan mencari solusi lain sampai saya menemukan kelas op ini. Ini adalah pekerja mukjizat batas.
IamIC
1
@IAMIC apakah itu berarti saya tidak perlu repot-repot mengindeks array string? Dan saya hanya harus mengindeks array integer?
ryan2johnson9
93

@Tregoreg mengajukan pertanyaan dalam komentar untuk hadiah yang ditawarkannya:

Saya tidak menemukan jawaban saat ini berfungsi. Menggunakan indeks GIN pada kolom array-typed tidak meningkatkan kinerja operator ANY (). Benarkah tidak ada solusi?

@ Jawaban diterima Frank memberitahu Anda untuk menggunakan operator array , yang masih benar untuk Postgres 11. Manual:

... distribusi standar PostgreSQL mencakup kelas operator GIN untuk array, yang mendukung kueri yang diindeks menggunakan operator ini:

<@
@>
=
&&

Daftar lengkap kelas operator bawaan untuk indeks GIN dalam distribusi standar ada di sini.

Dalam indeks Postgres terikat ke operator (yang diterapkan untuk jenis tertentu), bukan tipe data saja atau fungsi atau apa pun. Itu adalah warisan dari desain Berkeley asli Postgres dan sangat sulit untuk diubah sekarang. Dan itu umumnya bekerja dengan baik. Berikut adalah utas tentang pgsql-bug dengan Tom Lane mengomentari ini.

Beberapa fungsi PostGis (seperti ST_DWithin()) tampaknya melanggar prinsip ini, tetapi tidak demikian. Fungsi-fungsi tersebut ditulis ulang secara internal untuk menggunakan masing-masing operator .

Ekspresi yang diindeks harus di sebelah kiri operator. Untuk sebagian besar operator ( termasuk semua yang di atas ) perencana kueri dapat mencapai ini dengan membalik operan jika Anda menempatkan ekspresi yang diindeks ke kanan - mengingat bahwa COMMUTATORtelah ditentukan. The ANYmembangun dapat digunakan dalam kombinasi dengan berbagai operator dan tidak operator itu sendiri. Ketika digunakan sebagai constant = ANY (array_expression)indeks saja yang mendukung =operator pada elemen array akan memenuhi syarat dan kami akan membutuhkan komutator untuk = ANY(). Indeks GIN keluar.

Postgres saat ini tidak cukup pintar untuk mendapatkan ekspresi yang bisa diindeks GIN darinya. Sebagai permulaan, constant = ANY (array_expression)adalah tidak benar-benar setara dengan array_expression @> ARRAY[constant]. Operator array mengembalikan kesalahan jika ada elemen NULL yang terlibat, sementara ANYkonstruk dapat menangani NULL di kedua sisi. Dan ada hasil yang berbeda untuk ketidakcocokan tipe data.

Jawaban terkait:

Selain itu

Saat bekerja dengan integerarray ( int4, bukan int2atau int8) tanpa NULLnilai (seperti contoh Anda menyiratkan) pertimbangkan modul tambahan intarray, yang menyediakan operator khusus, lebih cepat dan dukungan indeks. Lihat:

Adapun UNIQUEkendala dalam pertanyaan Anda yang tidak dijawab: Itu diterapkan dengan indeks btree pada seluruh nilai array (seperti yang Anda duga) dan tidak membantu dengan pencarian elemen sama sekali. Detail:

Erwin Brandstetter
sumber
1
Aaaaaaah, merasa sangat malu sekarang, tapi itu tidak terlintas di pikiran saya bahwa postgres tidak akan menggunakan indeks bahkan jika secara teori memungkinkan. Mungkin itu juga karena kurangnya wawasan saya tentang postgres, seperti bahwa indeks terikat pada operator. Terima kasih telah meluangkan waktu untuk menjawab pertanyaan saya yang salah dan membagikan pengetahuan Anda!
Tregoreg
6
@Tregoreg: Jangan terlalu malu, itu benar-benar tidak terlalu jelas. Saya ingat menjadi bingung oleh ini sendiri ketika saya pertama kali bertemu dengannya. Pertanyaan dan klarifikasi yang ditambahkan harus cukup bermanfaat bagi masyarakat umum.
Erwin Brandstetter
1
Dari pengalaman saya, indeks ini menawarkan sedikit atau tidak ada percepatan kecuali gin__int_ops digunakan untuk integer[]kolom. Butuh bertahun-tahun frustrasi dan mencari solusi lain sampai saya menemukan kelas op ini. Ini adalah pekerja mukjizat batas.
IamIC
2
@IAMIC: Saya menambahkan pointer ke intarray. Tampak penting, seperti yang Anda tunjukkan.
Erwin Brandstetter
Untuk ANY (array_expression) = constantekspresi, indeks GIN berfungsi dengan baik?
user10375
37

Sekarang mungkin untuk mengindeks elemen array individual. Sebagai contoh:

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)

Ini berfungsi setidaknya pada Postgres 9.2.1. Perhatikan bahwa Anda perlu membuat indeks terpisah untuk setiap indeks array, dalam contoh saya, saya hanya mengindeks elemen pertama.

Ed4
sumber
28
Jangan sampai hilang - pendekatan ini tidak ada harapan untuk array panjang variabel di mana Anda ingin menggunakan operator ANY ().
Καrτhικ
24
Ini benar-benar tidak terlalu berguna. Jika Anda memiliki jumlah elemen array yang tetap, Anda lebih suka menggunakan kolom individual untuk setiap elemen (dan indeks btree polos) daripada membangun indeks ekspresi yang lebih mahal untuk setiap item array. Penyimpanan kolom individual jauh lebih murah tanpa overhead array juga.
Erwin Brandstetter