Indeks max ukuran baris kesalahan

12

Apakah ada batas atas untuk arraykolom?

Saya mendapatkan kesalahan ini saat memasukkan ke dalam bidang array -

PG::Error: ERROR:  index row size 3480 exceeds maximum 2712 for index "ix_data"

Inilah definisi tabel saya -

create table test_array(id varchar(50), data text[]);

ALTER TABLE test_array ADD PRIMARY KEY (id);

CREATE INDEX ix_data ON test_array USING GIN (data);

Saya perlu indeks pada bidang array, karena saya sedang melakukan beberapa pencarian di atasnya.


sumber
Mungkinkah yang databerisi daftar tag seperti ditunjukkan dalam posting blog terkait ini oleh Scott Snyder ? Jika itu masalahnya, saya mungkin punya solusi yang lebih baik untuk Anda.
Erwin Brandstetter
user310525, saya ingin memberi saran kedua kepada Erwin bahwa ini akan lebih baik di dba.se, jika Anda bersedia membuat akun di sana dan memberi tanda kepada moderator untuk bermigrasi?
Jack bilang coba topanswers.xyz

Jawaban:

14

Masalah

Berikut adalah kasus yang sangat mirip yang dibahas di pgsql.general . Ini tentang batasan dalam indeks b-tree, tapi itu semua sama karena indeks GIN menggunakan indeks b-tree untuk kunci secara internal dan oleh karena itu mengalami batasan yang sama untuk ukuran kunci (bukan ukuran item dalam b-tree polos indeks).

Saya mengutip manual tentang implementasi indeks GIN :

Secara internal, indeks GIN berisi indeks B-tree yang dibangun di atas kunci, di mana setiap kunci adalah elemen dari satu atau lebih item yang diindeks

Either way, setidaknya satu elemen array di kolom Anda dataterlalu besar untuk diindeks. Jika ini hanya nilai aneh tunggal atau semacam kecelakaan, Anda mungkin dapat memotong nilainya dan dilakukan dengan itu.

Untuk keperluan demo berikut, saya akan menganggap sebaliknya: banyak nilai teks panjang dalam array.

Solusi sederhana

Anda bisa mengganti elemen dalam array Anda datadengan nilai hash yang sesuai . Dan mengirim nilai pencarian melalui fungsi hash yang sama. Tentu saja, Anda mungkin ingin menyimpan dokumen asli Anda di suatu tempat. Dengan itu, kami hampir tiba di varian kedua saya ...

Solusi canggih

Anda bisa membuat tabel pencarian untuk elemen array dengan serialkolom sebagai pengganti kunci primer (secara efektif jenis nilai hash radikal) - yang lebih menarik jika nilai elemen yang terlibat tidak unik:

CREATE TABLE elem (
  elem_id serial NOT NULL PRIMARY KEY
, elem    text UNIQUE NOT NULL
);

Karena kita ingin mencari elem, kita menambahkan indeks - tapi sebuah indeks pada ekspresi saat ini, dengan hanya 10 karakter pertama dari teks yang panjang. Itu seharusnya cukup dalam kebanyakan kasus untuk mempersempit pencarian ke satu atau beberapa hits. Sesuaikan ukuran dengan distribusi data Anda. Atau gunakan fungsi hash yang lebih canggih.

CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));

Kolom Anda datakemudian akan bertipe int[]. Saya mengganti nama tabel datadan menyingkirkan yang tidak menyenangkan yang varchar(50)Anda miliki dalam contoh Anda:

CREATE TEMP TABLE data(
  data_id serial PRIMARY KEY
, data int[]
);

Setiap elemen array datamengacu pada a elem.elem_id. Pada titik ini, Anda dapat mempertimbangkan untuk mengganti kolom array dengan tabel n: m, sehingga menormalkan skema Anda dan memungkinkan Postgres untuk menegakkan integritas referensial. Pengindeksan dan penanganan umum menjadi lebih mudah ...

Namun, untuk alasan kinerja, int[]kolom dalam kombinasi dengan indeks GIN mungkin lebih unggul. Ukuran penyimpanan jauh lebih kecil. Dalam hal ini kita membutuhkan indeks GIN:

CREATE INDEX data_data_gin_idx ON data USING GIN (data);

Sekarang, setiap kunci indeks GIN (= elemen array) adalah integerbukan gondrong text. Indeks akan lebih kecil dengan beberapa urutan besarnya, pencarian akan lebih cepat.

The downside: sebelum Anda benar-benar dapat melakukan pencarian, Anda harus melihat elem_iddari tabel elem. Menggunakan indeks fungsional saya yang baru diperkenalkan elem_elem_left10_idx, ini juga akan jauh lebih cepat.

Anda dapat melakukan semuanya dalam satu permintaan sederhana :

SELECT d.*, e.*
FROM   elem e
JOIN   data d ON ARRAY[e.elem_id] <@ d.data
WHERE  left(e.elem, 10) = left('word1234word', 10) -- match index condition
AND    e.elem = 'word1234word';  -- need to recheck, functional index is lossy

Anda mungkin tertarik pada ekstensi intarray, yang memasok operator tambahan dan kelas operator.

Demo langsung yang berfungsi penuh di sqlfiddle.

Erwin Brandstetter
sumber
2

Kesalahannya adalah dengan indeks ix_data, bukan text[]bidang. Ukuran maksimum untuk satu baris dalam tipe indeks tertentu terbatas pada 2712byte. Jika Anda menjatuhkan indeks dan mencoba memasukkan lagi, itu akan bekerja untuk Anda. Jika Anda perlu mengindeks bidang yang lebih besar, Anda mungkin ingin melihat fitur pengindeksan teks lengkap postgres.

jcern
sumber
2

Saya mendapatkan ini di kolom geografi PostGIS. Itu karena saya tidak sengaja membuat indeks salah. Anda harus memasukkan parameter MENGGUNAKAN GIST saat membuat indeks tersebut.

Brad Mathews
sumber
Terima kasih - itu dia! Wow, sejauh ini dibuat-buat. Mungkin telah menyelamatkan saya berjam-jam. Terutama karena saya pikir GiST digunakan secara default tetapi saya salah dan mencoba menggunakan b-tree.
Jonas