正しい道
正規化を再検討することをお勧めします あなたのスキーマ。誰もが「最も単純なクエリでも参加する」必要はありません。 。 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配列に存在するかどうかを確認します