編集で明らかになったように、拡張機能 btree_gist コード>
。これがないと、例は name WITH =
ですでに失敗します。 。
CREATE EXTENSION btree_gist;
btree_gist
によってインストールされた演算子クラス 多くのオペレーターをカバーします。残念ながら、&
演算子はそれらの中にありません。明らかに、 boolean
を返さないためです。 これは、オペレーターが資格を得ることが期待されます。
代替ソリューション
bツリーの複数列のインデックスを組み合わせて使用します (速度のために)そしてトリガー 代わりは。 PostgreSQL 9.1でテストされたこのデモを検討してください :
CREATE TABLE t (
name text
,value bit(8)
);
INSERT INTO t VALUES ('a', B'10101010');
CREATE INDEX t_name_value_idx ON t (name, value);
CREATE OR REPLACE FUNCTION trg_t_name_value_inversion_prohibited()
RETURNS trigger AS
$func$
BEGIN
IF EXISTS (
SELECT 1 FROM t
WHERE (name, value) = (NEW.name, ~ NEW.value) -- example: exclude inversion
) THEN
RAISE EXCEPTION 'Your text here!';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef_t_name_value_inversion_prohibited
BEFORE INSERT OR UPDATE OF name, value -- only involved columns relevant!
ON t
FOR EACH ROW
EXECUTE PROCEDURE trg_t_name_value_inversion_prohibited();
INSERT INTO t VALUES ('a', ~ B'10101010'); -- fails with your error msg.
-
拡張子
btree_gist
ではありません このシナリオでは必要です。 -
チェック制約 動作しません。
CREATE TABLE
のマニュアルを引用します :大胆な強調鉱山:
bツリーインデックスの保守はGiSTインデックスよりも安価であるため、実際には除外制約よりも優れたパフォーマンスを発揮するはずです。そして、基本的な =
でのルックアップ 演算子は、&
を使用した架空のルックアップよりも高速である必要があります 演算子。
このソリューションは、除外制約ほど安全ではありません。これは、トリガーを簡単に回避できるためです。たとえば、同じイベントの後続のトリガーで、またはトリガーが一時的に無効になっている場合などです。このような条件が当てはまる場合は、テーブル全体に対して追加のチェックを実行する準備をしてください。
より複雑な状態
トリガーの例は、 value
の反転のみをキャッチします 。コメントで明確にしたように、実際には代わりに次のような条件が必要です。
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
この条件は少し高価ですが、それでもインデックスを使用できます。上からの複数列のインデックスは機能します-とにかくそれが必要な場合。または、もう少し効率的に、名前の単純なインデックス:
CREATE INDEX t_name_idx ON t (name);
コメントしたように、 name
ごとに最大8つの異なる行しか存在できません。 、実際には少ない。したがって、これはまだ高速である必要があります。
究極のINSERTパフォーマンス
INSERT
の場合 パフォーマンスが最も重要です。特に、試行されたINSERTの多くが条件に失敗した場合は、さらに多くのことを実行できます。事前に集計された value
のマテリアライズドビューを作成します。 name
ごと :
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
名前コード> ここでユニークであることが保証されています。
PRIMARY KEY
を使用します name
求めているインデックスを提供するには:
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
次に、 INSERT
次のようになります:
WITH i(n,v) AS (SELECT 'a'::text, B'10101010'::bit(8))
INSERT INTO t (name, value)
SELECT n, v
FROM i
LEFT JOIN mv_t m ON m.name = i.n
AND m.value & i.v <> B'00000000'::bit(8)
WHERE m.n IS NULL; -- alternative syntax for EXISTS (...)
fillfactor
テーブルが多くの更新を取得する場合にのみ役立ちます。
名前、値、または削除の挿入または更新後の TRIGGER
のマテリアライズドビューの行を更新します それを最新に保つために。追加のオブジェクトのコストは、ゲインと比較検討する必要があります。通常の負荷に大きく依存します。