8

私のデータベース レイアウトでは、新しい顧客ごとに新しいスキーマを作成する必要があります。現在、ネットで見つけた内部関数を使用し、少し変更しています。

CREATE FUNCTION copy_schema(
    source_schema character varying, 
    target_schema character varying, 
    copy_data boolean)
RETURNS integer AS
$BODY$
DECLARE
    t_ex integer := 0;
    s_ex integer := 0;
    src_table character varying;
    trg_table character varying;
BEGIN
    if (select 1 from pg_namespace where nspname = source_schema) THEN
        -- we have defined target schema
        s_ex := 1;
    END IF;

    IF (s_ex = 0) THEN
        -- no source schema exist
        RETURN 0;
    END IF;

    if (select 1 from pg_namespace where nspname = target_schema) THEN
        -- we have defined target schema need to sync all table layout
        t_ex := 1;
    ELSE
        EXECUTE 'CREATE SCHEMA '||target_schema||' AUTHORIZATION user';
    END IF;

    FOR src_table IN 
       SELECT table_name 
       FROM information_schema.TABLES 
       WHERE table_schema = source_schema
    LOOP
        trg_table := target_schema||'.'||src_table;
        EXECUTE 
            'CREATE TABLE ' || trg_table || ' (LIKE ' || source_schema || '.' || src_table || ' INCLUDING ALL)';
        IF (copy_data = true) THEN
            EXECUTE 'INSERT INTO ' || trg_table || '(SELECT * FROM ' || source_schema || '.' || src_table || ')';
        END IF;
    END LOOP;

    return t_ex;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

このスクリプトの問題は、新しいスキーマのテーブルが引き続きソース スキーマのシーケンスを使用することです。新しく作成されたテーブルのシーケンスの新しいコピー (またはスキーマ全体を複製する別の信頼できる方法) を取得するために SQL ステートメント (または他の信頼できる方法) を使用する方法はありますか?

4

3 に答える 3

7

問題の根本

古いシーケンスへの接続は、関連する列のプレーンなデフォルト値から取得されます。ここでマニュアルを引用します:

コピーされた列定義のデフォルトの式は、 が指定されている場合にのみコピーさINCLUDING DEFAULTSれます。デフォルトの動作では、デフォルトの式が除外されるため、新しいテーブルにコピーされた列のデフォルトは null になります。

で新しいテーブルを作成するので

INCLUDING ALL

と:

INCLUDING ALLの省略形ですINCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS

..同じデフォルトを取得します。デフォルトを除外するかnextval()、新しいテーブルを作成した後にそれらのデフォルト値を明示的に変更することができます。中間点はないと思います。


ダンプ/ダンプのハック/復元でよりシンプルに

またはスキーマ全体を複製する別の信頼できる方法

次のように schema のスキーマ (同じ単語、異なる意味) をダンプできますpg_dump

pg_dump $DB -p $PORT -n $SCHEMA -sf /var/lib/postgresql/your_name.pgsql

ダンプをハックします (つまり、テキスト エディターを使用するか、スクリプトを作成します): ダンプの上部にあるスキーマ名を交換し、他のすべての出現SET search_path箇所をシーケンスのスキーマ修飾として交換します。スキーマに一意の名前を選択した場合は、お気に入りのツール (または ...) を使用してグローバル検索と置換を 1 回実行するsedだけvimで問題は解決します。

psql次に、同じデータベースまたは他のデータベースに対してSQL スクリプトを実行します。

psql $DB -p $PORT -f /var/lib/postgresql/your_name.pgsql > /dev/null

私が最初に投稿したものとは反対に、シリアル列はまだダンプで分割されています (少なくとも PostgreSQL 9.1.5 では)。SQL スクリプトはシーケンスを個別に作成し、次のようにシリアル列に添付します。

ALTER SEQUENCE seq OWNED BY tbl.col;

デフォルト値を個別に設定します。

余談ですserialが、すべての要件が満たされている場合、DDL スクリプトの pgAdmin リバース エンジニア列の現在のバージョン。

于 2012-09-24T20:27:11.323 に答える
6

そして、少し考えた後、最初の投稿で述べたSQL関数の更新に沿ったので、今では次のようになります。

CREATE FUNCTION copy_schema(
    source_schema character varying, 
    target_schema character varying, 
    copy_data boolean)
RETURNS integer AS
$BODY$
DECLARE
    t_ex integer := 0;
    s_ex integer := 0;
    src_table character varying;
    trg_table character varying;
BEGIN
    if (select 1 from pg_namespace where nspname = source_schema) THEN
        -- we have defined target schema
        s_ex := 1;
    END IF;

    IF (s_ex = 0) THEN
        -- no source schema exist
        RETURN 0;
    END IF;

    if (select 1 from pg_namespace where nspname = target_schema) THEN
        -- we have defined target schema need to sync all table layout
        t_ex := 1;
    ELSE
        EXECUTE 'CREATE SCHEMA '||target_schema||' AUTHORIZATION user';
    END IF;

    FOR src_table IN 
        SELECT table_name 
        FROM information_schema.TABLES 
        WHERE table_schema = source_schema
    LOOP
        trg_table := target_schema||'.'||src_table;
        EXECUTE 'CREATE TABLE ' || trg_table || ' (LIKE ' || source_schema || '.' || src_table || ' INCLUDING ALL)';
        EXECUTE 'CREATE SEQUENCE ' || trg_table || '_id_seq OWNED BY '||trg_table || '.id';
        EXECUTE 'ALTER TABLE ' || trg_table || ' ALTER COLUMN id SET DEFAULT nextval('''|| trg_table || '_id_seq''::regclass)';
        IF (copy_data = true) THEN
            EXECUTE 'INSERT INTO ' || trg_table || '(SELECT * FROM ' || source_schema || '.' || src_table || ')';
        END IF;
    END LOOP;
    return t_ex;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

これは誰にとっても普遍的なソリューションではありませんが、スキーマ内のすべてのテーブルに id という名前のシリアル フィールドがあるため、私には適しています。

@erwin-brandstetter によって提案された、ダンプ / ハック ダンプ ファイル / 再びダンプ ファイルを元に戻すバージョンは、フォーラムでよく見られる方法です。

専用サーバーの場合は機能しますが、共有ホスティングの場合 (または外部スクリプトへの依存を少なくする必要がある場合) は、内部機能の方が優れているようです。

于 2012-09-24T22:15:52.607 に答える