6

テーブルがあり、EVENTS と呼びます。各行は、テーブル内の 0 個以上の他の行に依存できます。循環依存 (つまり、同じグループ内のイベントに戻るイベントのグループ) も防止する、この関係を表す方法が必要です。

現在、EVENTS の外部にリンク テーブルがあり、EVENTS_DEP と呼んでいます。このテーブルは、依存する行をそれらが依存する行にリンクし、1 つの行に複数の依存関係を許可します。このようなテーブルを使用して循環依存を防ぐにはどうすればよいですか?

注: データベース設計のみでこれを行うことができる場合 (スクリプト、トリガーなどではなく)、これが理想的です。

また、これがトリガーを使用してのみ実行できる場合は、どの種類のトリガー (つまり、どのイベントで) で実行する必要があるか (挿入時か?) を教えてください。

4

2 に答える 2

8

これを確認するには、トリガーを挿入します。

次のテーブル構造を想定

CREATE TABLE event (
    id bigserial PRIMARY KEY,
    foo varchar
);

CREATE TABLE event_deps (
    parent bigint REFERENCES event(id),
    child bigint REFERENCES event(id),

    PRIMARY KEY (parent, child),
    CHECK (parent <> child)
);

次のINSERTトリガーが必要になります

CREATE FUNCTION deps_insert_trigger_func() RETURNS trigger AS $BODY$
    DECLARE
        results bigint;
    BEGIN
        WITH RECURSIVE p(id) AS (
            SELECT parent
                FROM event_deps
                WHERE child=NEW.parent
            UNION
            SELECT parent
                FROM p, event_deps d
                WHERE p.id = d.child
            )
        SELECT * INTO results
        FROM p
        WHERE id=NEW.child;

        IF FOUND THEN 
            RAISE EXCEPTION 'Circular dependencies are not allowed.';
        END IF;
        RETURN NEW;
    END;
$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER before_insert_event_deps_trg BEFORE INSERT ON event_deps
    FOR EACH ROW
    EXECUTE PROCEDURE deps_insert_trigger_func();

親Aと子Bの間に新しいリンクが追加されると、AWITHRECURSIVEクエリを使用してAのすべての祖先が検索されます。Bはそれらの1つであってはなりません。

古いリンクに対してトリガーが実行されたときにまだそこにあるため、UPDATEトリガーを使用すると、INSERTトリガーからのテストで誤検知が発生する可能性があるため、UPDATEトリガーはより困難になります。

したがって、UPDATEの場合、古いデータを非表示にするための条件を追加する必要があります。

CREATE FUNCTION deps_update_trigger_func() RETURNS trigger AS $BODY$
    DECLARE
        results bigint;
    BEGIN
        WITH RECURSIVE p(id) AS (
            SELECT parent
                FROM event_deps
                WHERE child=NEW.parent
                    AND NOT (child = OLD.child AND parent = OLD.parent) -- hide old row
            UNION
            SELECT parent
                FROM p, event_deps d
                WHERE p.id = d.child
                    AND NOT (child = OLD.child AND parent = OLD.parent) -- hide old row
            )
        SELECT * INTO results
        FROM p
        WHERE id=NEW.child;

        IF FOUND THEN 
            RAISE EXCEPTION 'Circular dependencies are not allowed.';
        END IF;
        RETURN NEW;
    END;
$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER before_update_event_deps_trg BEFORE UPDATE ON event_deps
    FOR EACH ROW
    EXECUTE PROCEDURE deps_update_trigger_func();
于 2012-07-30T16:29:47.433 に答える
1

SQL エンジンを使用し、トリガーをプログラミングせずにこれを行うことはできません。SQl エンジンがこれをサポートするには、再帰 SQL (別名、再帰 WITH 式、または再帰 CTE) をサポートし、信頼できる ASSERTION をサポートする必要があります。

多くは CTE の /WITH 式をサポートしていますが、おそらくすべてが機能の再帰バージョンをサポートしているわけではありません。ASSERTION については、それらをサポートするシステムは 1 つしかないと言われていますが、実装には欠陥があり、バグが多いため、真剣に使用することを検討するのはばかげています。

要求どおりに正確に実行できるシステムがありますが、そのようなシステムと SQL をやり取りすることを期待しないでください。このようなシステムの作成者は、赤ちゃんの関係を維持することに誇りを持っています。

于 2012-07-28T17:54:46.503 に答える