拡張機能をインストールしましたbtree_gist
。それがなければ、この例は ですでに失敗しname WITH =
ます。
CREATE EXTENSION btree_gist;
によってインストールされる演算子クラスは、btree_gist
多くの演算子をカバーしています。残念ながら、&
オペレーターはその中にいません。boolean
明らかに、修飾する演算子に期待されるa を返さないためです。
代替ソリューション
代わりに、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
LANGUAGE plpgsql AS
$func$
BEGIN
IF EXISTS (
SELECT FROM t
WHERE (name, value) = (NEW.name, ~ NEW.value) -- example: exclude inversion
) THEN
RAISE EXCEPTION 'Your text here!';
END IF;
RETURN NEW;
END
$func$;
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 FUNCTION trg_t_name_value_inversion_prohibited();
INSERT INTO t VALUES ('a', ~ B'10101010'); -- fails with your error msg.
Postgres 10 以前では、代わりに次を使用します。
...
EXECUTE PROCEDURE trg_t_name_value_inversion_prohibited();
見る:
~
は逆演算子です。
このシナリオでは、拡張子btree_gist
は必要ありません。
効率を高めるために、トリガーをINSERT OR UPDATE OF
関連する列に制限しました。
チェック制約は機能しません。のマニュアルCREATE TABLE
を引用します:
現在、式にサブクエリを含めることも、現在の行のCHECK
列以外の変数を参照することもできません。
大胆強調鉱山。
bツリーインデックスのメンテナンスはGiSTインデックスよりも安価であるため、実際には除外制約よりも優れたパフォーマンスを発揮するはずです。また、基本的な=
演算子を使用したルックアップは、演算子を使用した仮想のルックアップよりも高速である必要があります&
。
このソリューションは、除外制約ほど確実ではありません。たとえば、同じイベントの後続のトリガーで、またはトリガーが一時的に無効になっている場合など、トリガーはより簡単に回避できるためです。そのような条件が適用される場合は、テーブル全体で追加のチェックを実行する準備をしてください。
より複雑な条件
サンプル トリガーは、 の反転のみをキャッチしますvalue
。コメントで明確にしたように、実際には代わりに次のような条件が必要です。
IF EXISTS (
SELECT FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
この条件は少しコストが高くなりますが、それでもインデックスを使用できます。上記の複数列インデックスは機能します-とにかくそれを使用している場合。または、より効率的に、名前の単純なインデックス:
CREATE INDEX t_name_idx ON t (name);
あなたは、 ごとに最大 8 つの異なる行しか存在できないとコメントしましたがname
、実際にはそれより少なくなります。したがって、これはまだ速いはずです。
究極の 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;
name
ここで一意であることが保証されています。PRIMARY KEY
onを使用してname
、目的のインデックスを提供します。
ALTER TABLE mv_t SET (FILLFACTOR=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name);
次に、次の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 AFTER INSERT OR UPDATE OF name, value OR DELETE
状態に保ちます。追加オブジェクトのコストとゲインは慎重に検討する必要があります。典型的な負荷に大きく依存します。