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

PostgreSQLは配列要素に一意性の制約を設けることができますか?

    正しい道

    正規化を再検討することをお勧めします あなたのスキーマ。誰もが「最も単純なクエリでも参加する」必要はありません。 。 VIEWを作成します そのために。

    テーブルは次のようになります:

    CREATE TABLE hostname (
      hostname_id serial PRIMARY KEY
    , host_id     int  REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
    , hostname    text UNIQUE
    );
    

    代理主キーhostname_id オプション 。私はそれを持っているのが好きです。あなたの場合、hostname 主キーになる可能性があります。しかし、多くの操作は、単純で小さなintegerを使用すると、より高速になります。 鍵。テーブルhostにリンクする外部キー制約を作成します 。
    次のようなビューを作成します:

    CREATE VIEW v_host AS
    SELECT h.*
         , array_agg(hn.hostname) AS hostnames
    --   , string_agg(hn.hostname, ', ') AS hostnames  -- text instead of array
    FROM   host h
    JOIN   hostname hn USING (host_id)
    GROUP  BY h.host_id;   -- works in v9.1+
    

    pg 9.1以降 、GROUP BY主キー SELECT内のそのテーブルのすべての列をカバーします リスト。バージョン9.1のリリースノート:

    GROUP BY以外を許可する GROUP BYで主キーが指定されている場合のクエリターゲットリストの列 条項

    クエリは、テーブルのようにビューを使用できます。ホスト名の検索は多くになります この方法でより速く:

    SELECT *
    FROM   host h
    JOIN   hostname hn USING (host_id)
    WHERE  hn.hostname = 'foobar';
    

    Postgresで9.2+ インデックスのみのスキャンを取得できれば、複数列のインデックスの方がさらに優れています。 それから:

    CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
    

    Postgres 9.3以降 、 MATERIALIZED VIEWを使用できます 、状況が許せば。特に、テーブルに書き込むよりもはるかに頻繁に読み取る場合。

    ダークサイド(あなたが実際に尋ねたこと)

    私があなたに正しい道を納得させることができないならば、私も暗い側で援助します。私は柔軟です。 :)

    これは、ホスト名の一意性を強制する方法のデモです。テーブルhostnameを使用します テーブルhostでホスト名とトリガーを収集します それを最新の状態に保つために。固有の違反は例外を発生させ、操作を中止します。

    CREATE TABLE host(hostnames text[]);
    CREATE TABLE hostname(hostname text PRIMARY KEY);  --  pk enforces uniqueness
    

    トリガー機能:

    CREATE OR REPLACE FUNCTION trg_host_insupdelbef()
      RETURNS trigger AS
    $func$
    BEGIN
    -- split UPDATE into DELETE & INSERT
    IF TG_OP = 'UPDATE' THEN
       IF OLD.hostnames IS DISTINCT FROM NEW.hostnames THEN  -- keep going
       ELSE RETURN NEW;  -- exit, nothing to do
       END IF;
    END IF;
    
    IF TG_OP IN ('DELETE', 'UPDATE') THEN
       DELETE FROM hostname h
       USING  unnest(OLD.hostnames) d(x)
       WHERE  h.hostname = d.x;
    
       IF TG_OP = 'DELETE' THEN RETURN OLD;  -- exit, we are done
       END IF;
    END IF;
    
    -- control only reaches here for INSERT or UPDATE (with actual changes)
    INSERT INTO hostname(hostname)
    SELECT h
    FROM   unnest(NEW.hostnames) h;
    
    RETURN NEW;
    END
    $func$ LANGUAGE plpgsql;
    

    トリガー:

    CREATE TRIGGER host_insupdelbef
    BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
    FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
    

    SQLフィドル テスト実行あり。

    GINインデックスを使用する 配列列host.hostnames および配列演算子 それを操作するには:

    • PostgreSQL配列インデックスが使用されないのはなぜですか(Rails 4)?
    • 指定された値の配列のいずれかがPostgres配列に存在するかどうかを確認します


    1. SQLで外部キー制約を削除する方法

    2. T-SQLで月末を取得する方法

    3. OracleSQLDeveloperを使用したCLOBのテキストファイルへのエクスポート

    4. MySQLクエリ-今日から過去30日間の記録