Vấn đề
Đây là một trường hợp rất giống nhau được thảo luận trên pssql.general . Đó là về giới hạn trong chỉ mục cây b, nhưng tất cả đều giống nhau vì chỉ số GIN sử dụng chỉ mục cây b cho các khóa bên trong và do đó chạy vào cùng giới hạn cho kích thước khóa (thay vì kích thước mục trong cây b đơn giản mục lục).
Tôi trích dẫn hướng dẫn thực hiện chỉ số GIN :
Trong nội bộ, một chỉ mục GIN chứa chỉ mục cây B được xây dựng trên các khóa, trong đó mỗi khóa là một thành phần của một hoặc nhiều mục được lập chỉ mục
Dù bằng cách nào, ít nhất một phần tử mảng trong cột của bạn data
quá lớn để được lập chỉ mục. Nếu đây chỉ là một giá trị kỳ dị hoặc một loại tai nạn nào đó, bạn có thể cắt bớt giá trị và được thực hiện với nó.
Với mục đích của bản demo sau, tôi sẽ giả sử khác: rất nhiều giá trị văn bản dài trong mảng.
Giải pháp đơn giản
Bạn có thể thay thế các phần tử trong mảng của bạn data
bằng các giá trị băm theo . Và gửi các giá trị tra cứu thông qua chức năng băm tương tự. Tất nhiên, bạn có thể muốn lưu trữ bản gốc của bạn ngoài một nơi nào đó. Cùng với đó, chúng tôi gần như đến biến thể thứ hai của mình ...
Giải pháp tiên tiến
Bạn có thể tạo bảng tra cứu cho các phần tử mảng với một serial
cột là khóa chính thay thế (thực sự là một loại giá trị băm triệt để) - điều này thú vị hơn nếu các giá trị phần tử liên quan không phải là duy nhất:
CREATE TABLE elem (
elem_id serial NOT NULL PRIMARY KEY
, elem text UNIQUE NOT NULL
);
Kể từ khi chúng tôi muốn tìm kiếm elem
, chúng tôi thêm một chỉ số - nhưng một chỉ mục trên một biểu thời gian này, chỉ với 10 ký tự đầu tiên của văn bản dài. Điều đó là đủ trong hầu hết các trường hợp để thu hẹp tìm kiếm xuống một hoặc một vài lần truy cập. Điều chỉnh kích thước để phân phối dữ liệu của bạn. Hoặc sử dụng hàm băm tinh vi hơn.
CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));
Cột của bạn data
sau đó sẽ là loại int[]
. Tôi đã đổi tên bảng thành data
và loại bỏ những điều đáng ngại varchar(50)
trong ví dụ của bạn:
CREATE TEMP TABLE data(
data_id serial PRIMARY KEY
, data int[]
);
Mỗi phần tử mảng trong data
tham chiếu đến a elem.elem_id
. Tại thời điểm này, bạn có thể xem xét thay thế cột mảng bằng bảng n: m, từ đó bình thường hóa lược đồ của bạn và cho phép Postgres thực thi tính toàn vẹn tham chiếu. Lập chỉ mục và xử lý chung trở nên dễ dàng hơn ...
Tuy nhiên, vì lý do hiệu suất, int[]
cột kết hợp với chỉ số GIN có thể vượt trội. Kích thước lưu trữ nhỏ hơn rất nhiều . Trong trường hợp này, chúng ta cần chỉ số GIN:
CREATE INDEX data_data_gin_idx ON data USING GIN (data);
Bây giờ, mỗi khóa của chỉ mục GIN (= phần tử mảng) là một integer
thay vì dài text
. Chỉ số sẽ nhỏ hơn theo một số bậc độ lớn, do đó các tìm kiếm sẽ nhanh hơn nhiều .
Nhược điểm: trước khi bạn thực sự có thể thực hiện tìm kiếm, bạn phải tra cứu elem_id
từ bảng elem
. Sử dụng chỉ số chức năng mới được giới thiệu của tôi elem_elem_left10_idx
, điều này cũng sẽ nhanh hơn nhiều.
Bạn có thể làm tất cả trong một truy vấn đơn giản :
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
Bạn có thể quan tâm đến phần mở rộng intarray
, cung cấp các toán tử bổ sung và các lớp toán tử.
data
chứa một danh sách các thẻ như được trình bày trong bài đăng trên blog liên quan này của Scott Snyder ? Nếu đó là trường hợp, tôi có thể có một giải pháp tốt hơn cho bạn.