web-dev-qa-db-ja.com

インデックスの最大行サイズエラー

array列に上限はありますか?

配列フィールドに挿入すると、このエラーが発生します-

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

これが私のテーブル定義です-

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

配列フィールドを検索しているので、配列フィールドにインデックスが必要です。

12
user310525

問題

これが pgsql.generalで説明されている非常に類似したケース です。これはbツリーインデックスの制限についてですが、GINインデックスは内部でkeysのbツリーインデックスを使用しているため、keysize(代わりにitemsize in a plain b-tree index)。

GINインデックスの実装に関するマニュアル を引用します。

内部的には、GINインデックスには、キー上に構築されたBツリーインデックスが含まれます。各キーは、1つ以上のインデックス付きアイテムの要素です

どちらの方法でも、列dataの少なくとも1つの配列要素が大きすぎるため、インデックスを作成できません。これが1つの異常な値または何らかの事故である場合は、値を切り捨てて処理できる可能性があります。

次のデモの目的のために、私はそれ以外の場合を想定します:配列内の多くの長いテキスト値。

シンプルなソリューション

配列dataの要素を対応するハッシュ値で置き換えることができます。そして、同じハッシュ関数を介してルックアップ値を送信します。もちろん、オリジナルをさらにどこかに保存したいと思うでしょう。これで、2番目のバリアントに到着します...

高度なソリューション

serial列が配列の要素のルックアップテーブルを代理主キー(実質的には急進的な種類のハッシュ値)として作成できます。これは、関係する要素の値が一意でない場合に、さらに興味深いものになります。

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

elemを検索するため、インデックスを追加します-but式のインデックス this長いテキストの最初の10文字のみを含む時間。これで、ほとんどの場合、検索を1つまたはいくつかのヒットに絞り込むことができます。サイズをデータ分布に適合させます。または、より高度なハッシュ関数を使用します。

_CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));
_

dataは、タイプ_int[]_になります。テーブルの名前をdataに変更し、例にある不吉なvarchar(50)を削除しました。

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

dataの各配列要素は_elem.elem_id_を参照します。この時点で、配列の列をn:mテーブルに置き換えることを検討してください。これにより、スキーマが正規化され、Postgresが参照整合性を適用できるようになります。インデックス作成と一般的な処理が簡単になります...

ただし、パフォーマンス上の理由から、GINインデックスと組み合わせた_int[]_列の方が優れている場合があります。ストレージサイズは大きく小さくなっています。この場合、GINインデックスが必要です。

_CREATE INDEX data_data_gin_idx ON data USING GIN (data);
_

これで、GINインデックス(=配列要素)の各keyは、長いintegerではなくtextになります。インデックスは数桁小さくなり、その結果、検索ははるかに高速になります

欠点:実際に検索を実行する前に、elemテーブルから_elem_id_を検索する必要があります。私が新しく導入した関数インデックス_elem_elem_left10_idx_を使用すると、これもはるかに高速になります。

あなたはそれを行うことができますすべて1つの簡単なクエリで:

_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
_

追加の演算子と演算子クラスを提供する拡張機能 intarray に興味があるかもしれません。

sqlfiddleの完全に機能するライブデモ

14

エラーは、ix_dataフィールドではなく、インデックスtext[]にあります。特定のインデックスタイプの行の最大サイズは、2712バイトに制限されています。インデックスを削除して挿入を再試行すると、うまくいくはずです。より大きなフィールドにインデックスを付ける必要がある場合は、postgresの全文インデックス付け機能を調べてください。

2
jcern

これはPostGIS地理カラムで取得していました。誤ってインデックスを作成してしまったからです。このようなインデックスを作成する場合は、USING Gistパラメーターを含める必要があります。

2
Brad Mathews