0

postgresql(9.1) での継承の実装について質問があります。

目的は、国、州、大陸を混ぜ合わせて「地域」を作成できる地理的階層モデルを構築することです。そして、これらの地域も国などと混同して、本当に素晴らしい地域階層を作成できます

したがって、私の論理モデルでは、すべてが一種の「場所」です。領域ツリーは、2 つの「場所」を使用してエッジワイズを指定することで構築できます。設計は以下のとおりで、Java 層で管理しやすくなっています。

    create table place_t (
            place_id serial primary key,
            place_type varchar(10)
    );
    create table country_t (
            short_name varchar(30) unique, 
            name varchar(255) null
    ) inherits(place_t);
    create table region_t( 
            short_name varchar(30),
            hierarchy_id integer, -- references hierarchy_t(hierarchy_id)
            unique(short_name) -- (short_name,hierarchy_id)
    ) inherits(place_t);
    create table region_hier_t( 
            parent integer references place_t(place_id), -- would prefer FK region_t(place_id)
            child integer references place_t(place_id),
            primary key(parent,child)
    );

    insert into region_t values(DEFAULT, 'region', 'NA', 'north american ops');
    insert into region_t values(DEFAULT, 'region', 'EMEA', 'europe and middle east');
    insert into country_t values(DEFAULT, 'country', 'US', 'USD', 'united states');
    insert into country_t values(DEFAULT, 'country', 'CN', 'CND', 'canada');

ここまでは順調ですね。しかし、以下は失敗します:

    insert  into region_hier_t
        select p.place_id, c.place_id
        from region_t as p, country_t as c
        where p.short_name = 'NA' and c.short_name = 'US';

その理由は、最初の 4 回の挿入で「place_t」に行が作成されなかったためです。RTFM!Postgres docs は実際にこれについて言及しています。

質問は - 回避策はありますか? region_t と country_t にトリガーを挿入して、独自の「継承」を実装することしか考えられません。

2 番目の質問は、このような混合ノード ツリー構造のより良い設計があるかどうかです。

特定の理由から、postgres-contrib 機能にあまり依存したくありません。ばかげているかもしれませんが、お気軽に声をかけてください。

ありがとう

4

3 に答える 3

1

参照が別のテーブルを呼び出す場合、country_t からキーを挿入できないため、region_hier_t テーブルの親列と子列の参照が間違っています(child integer references place_t(place_id))。それらを削除するか、新しいものを追加できます。それでは、2 番目のオプションを使用して、参照されるテーブル region_t と country_t の特定のキーに一致する一意の制約を追加しましょう。

ALTER TABLE region_t
  ADD CONSTRAINT pk_region_t PRIMARY KEY(place_id );

ALTER TABLE country_t
  ADD CONSTRAINT pk_country_t PRIMARY KEY(place_id );

region_hier_t の正しい CREATE ステートメントは次のとおりです。

        create table region_hier_t( 
            parent integer references region_t(place_id),
            child integer references country_t(place_id),
            primary key(parent,child)
    );

最後に、INSERT.

ご覧のとおり、多くの改善点があります。たぶん、あなたのデザインを再考する必要があります。この回答を見てください:郵便番号と政治部門を正規化された方法で保存する方法は? ソリューションよりもはるかに単純で、保守が容易です。

ただし、ソリューションにとどまりたい場合は、子テーブルに主キーを設定することを忘れないでください (上記のように)。チェック制約と非 null 制約のみがその子によって継承されますが、まだ行っていません。

他の挿入が正しく機能していないことがわかりました。

insert into region_t values(DEFAULT, 'region', 'NA', 'north american ops');


ERROR:  invalid input syntax for integer: "north american ops"
LINE 1: ...ert into region_t values(DEFAULT, 'region', 'NA', 'north ame...

したがって、列の割り当てにも問題があります。

于 2013-01-27T14:31:12.803 に答える
0

したがって、PostgreSQL での継承は、典型的な OOP 言語で使用される継承とは多少異なることがわかります。特に、「スーパークラス」テーブルは自動的に入力されません。それを行うために独自のトリガーを使用する必要がある場合、継承構造の使用例が残っていませんでした。

そこで、Postgresql の継承を放棄して、独自の「place_t」テーブルを作成しました。「country_t」、「state_t」、「county_t」、および「region_t」の子テーブルは、「place_id」を介して親の「place_t」にリンクされています。

これらの子テーブルで、「place_id」が「place_t」の有効な行を参照し、参照が後で変更されないようにするために、挿入/更新前の行レベル トリガーを作成しました。子テーブルの IOW、「place_id」は、1 回だけ書き込み、複数回読み取りのように動作する必要があります。

これで、ワールド ジオを挿入できます。また、新しい「リージョン」を定義します。地域階層の端を記録するために「region_composition_t」を作成しました。親は「region_t」への参照であり、子は「place_t」への参照です。

ここまでは順調ですね。現在の課題は、更新/削除のカスケード効果をどのように抑制するかです。

于 2013-02-07T02:44:57.963 に答える
0

回避策は、place_t への外部キーを取り除き、代わりに次のことを行うことです。

CREATE FUNCTION place_t_exists(id int)
RETURNS bool LANGUAGE SQL AS 
$$
   SELECT count(*) = 1 FROM place_t;
$$;

CREATE FUNCTION fkey_place_t() RETURNS TRIGGER
LANGUAGE PLPGSQL AS $$
BEGIN;
  IF place_t_exists(TG_ARGV[1]) THEN RETURN NEW
  ELSE RAISE EXCEPTION 'place_t does not exist';
  END IF;
END;
$$;

階層ノードが存在する場合、子テーブルに制限するものも必要です。

CREATE FUNCTION hierarchy_exists(id int) RETURNS BOOL LANGUAGE SQL AS
$$
SELECT COUNT(*) > 0 FROM region_heir_t WHERE parent = $1 or child = $1;
$$;

CREATE OR REPLACE FUNCTION fkey_hierarchy_trigger() RETURNS trigger LANGUAGE PLPGSQL AS
$$
BEGIN
   IF hierarchy_exists(old.place_id) THEN RAISE EXCEPTION 'Hierarchy node still exists';
   ELSE RETURN OLD;
END;
$$;

次に、トリガーを作成できます。

CREATE CONSTRAINT TRIGGER fkey_place_parent AFTER INSERT OR UPDATE TO region_hier_t
FOR EACH ROW EXECUTE PROCEDURE fkey_place_t(new.parent);
CREATE CONSTRAINT TRIGGER fkey_place_child AFTER INSERT OR UPDATE TO region_hier_t
FOR EACH ROW EXECUTE PROCEDURE fkey_place_t(new.child);

次に、place_t 子テーブルごとに次のようにします。

CREATE CONSTRAINT TRIGGER fkey_hier_t TO [child_table]
FOR EACH ROW EXECUTE PROCEDURE fkey_hierarchy_trigger();

このソリューションは価値がないかもしれませんが、必要に応じてその方法を知っておく価値はあります。

于 2013-03-02T14:52:54.257 に答える