sql >> データベース >  >> RDS >> PostgreSQL

ビットごとのAND演算子を使用したビット文字列列の除外制約

    編集で明らかになったように、拡張機能 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.
    

    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のマテリアライズドビューの行を更新します それを最新に保つために。追加のオブジェクトのコストは、ゲインと比較検討する必要があります。通常の負荷に大きく依存します。




    1. 値のリストに対する部分文字列のクエリ

    2. MySQL pidが終了しました(mysqlを起動できません)

    3. アノテーション付きのmysqlでmybatisを使用してインサートのIDを返す方法

    4. MySQLは行を動的な列数にピボットします