36

これに関するGoogleの結果は少し薄いですが、それは簡単には不可能であることを示唆しています.

私の具体的な問題は、テーブル B に「table_a_id」列が含まれるように、互いに関連する 2 つのテーブルの ID の番号を付け直す必要があることです。B の子が古い ID を指しているため、最初にテーブル A の番号を付け直すことはできません。最初にテーブル B の番号を付け直すことはできません。作成される前に新しい ID を指すことになるからです。3 つまたは 4 つのテーブルについて繰り返します。

「トランザクションを開始する、ref の整合性を無効にする、ID を整理する、ref の整合性を再度有効にする、トランザクションをコミットする」ことができる場合に、個々の関係をいじる必要はありません。Mysql と MSSQL の両方がこの機能 IIRC を提供しているので、Postgres が提供していなかったら驚くでしょう。

ありがとう!

4

7 に答える 7

48

できることは 2 つあります (これらは補完的なものであり、代替手段ではありません)。

  • 外部キー制約を DEFERRABLE として作成します。次に、「SET CONSTRAINTS DEFERRED;」を呼び出すと、トランザクションが終了するまで外部キー制約がチェックされなくなります。何も指定しない場合のデフォルトは NOT DEFERRABLE であることに注意してください (厄介なことに)。
  • 「ALTER TABLE mytable DISABLE TRIGGER ALL;」を呼び出して、データのロード中にトリガーが実行されないようにしてから、「ALTER TABLE mytable ENABLE TRIGGER ALL;」を呼び出します。それらを再度有効にすることが完了したら。
于 2008-09-26T14:46:47.947 に答える
34

制約を削除してから再作成するためのSQLを生成するこれら2つの優れたスクリプトを見つけました。どうぞ:

制約を削除するため

SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" DROP CONSTRAINT "'||conname||'";'
FROM pg_constraint 
INNER JOIN pg_class ON conrelid=pg_class.oid 
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace 
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname

それらを再現するために

SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" ADD CONSTRAINT "'||conname||'" '|| pg_get_constraintdef(pg_constraint.oid)||';'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END DESC,contype DESC,nspname DESC,relname DESC,conname DESC;

これらのクエリを実行すると、制約の削除と作成に必要な SQL スクリプトが出力されます。

制約を削除すると、テーブルで好きなことをすべて行うことができます。完了したら、それらを再紹介します。

于 2012-05-29T07:41:22.340 に答える
18

それは不可能に思えます。他の提案は、ほとんどの場合、制約を削除し、作業が完了した後に再作成することを指します。

DEFERRABLEただし、トランザクションが終了するまでチェックされないように、制約を作成できるようです。PostgreSQL のドキュメントをCREATE TABLE参照してください(「deferrable」を検索してください。ページの中央にあります)。

于 2008-09-26T14:41:22.950 に答える
5

これは、トランザクション内のすべての制約を削除し、いくつかのクエリを実行してから、それらすべての制約を再作成する Python スクリプトです。 pg_get_constraintdefこれは非常に簡単です:

class no_constraints(object):
    def __init__(self, connection):
        self.connection = connection

    def __enter__(self):
        self.transaction = self.connection.begin()
        try:
            self._drop_constraints()
        except:
            self.transaction.rollback()
            raise

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            self.transaction.rollback()
        else:
            try:
                self._create_constraints()
                self.transaction.commit()
            except:
                self.transaction.rollback()
                raise

    def _drop_constraints(self):
        self._constraints = self._all_constraints()

        for schemaname, tablename, name, def_ in self._constraints:
            self.connection.execute('ALTER TABLE "%s.%s" DROP CONSTRAINT %s' % (schemaname, tablename, name))

    def _create_constraints(self):
        for schemaname, tablename, name, def_ in self._constraints:
            self.connection.execute('ALTER TABLE "%s.%s" ADD CONSTRAINT %s %s' % (schamename, tablename, name, def_))

    def _all_constraints(self):
        return self.connection.execute("""
            SELECT n.nspname AS schemaname, c.relname, conname, pg_get_constraintdef(r.oid, false) as condef
                     FROM  pg_constraint r, pg_class c
                     LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                     WHERE r.contype = 'f'
                    and r.conrelid=c.oid
            """).fetchall()

if __name__ == '__main__':
    # example usage

    from sqlalchemy import create_engine

    engine = create_engine('postgresql://user:pass@host/dbname', echo=True)

    conn = engine.connect()
    with no_contraints(conn):
        r = conn.execute("delete from table1")
        print "%d rows affected" % r.rowcount
        r = conn.execute("delete from table2")
        print "%d rows affected" % r.rowcount
于 2010-04-29T19:02:30.890 に答える
5

外部キー制約のリストを作成し、それらを削除し、変更を加えてから、制約を再度追加する必要があると思います。alter table drop constraintおよびのドキュメントを確認してくださいalter table add constraint

于 2008-09-26T14:43:07.407 に答える
0

制約が の場合DEFERRABLE、これは非常に簡単です。トランザクション ブロックを使用して、FK 制約をトランザクションの開始時に延期するように設定するだけです。

http://www.postgresql.org/docs/9.4/static/sql-set-constraints.htmlから:

SET CONSTRAINTS は、現在のトランザクション内での制約チェックの動作を設定します。IMMEDIATE 制約は、各ステートメントの最後でチェックされます。DEFERRED 制約は、トランザクションがコミットされるまでチェックされません。

したがって、次のことができます。

BEGIN;

SET CONSTRAINTS
    table_1_parent_id_foreign, 
    table_2_parent_id_foreign,
    -- etc
DEFERRED;

-- do all your renumbering

COMMIT;

残念ながら、明示的に設定されてNOT DEFERRABLEいない限り、Postgres はデフォルトですべての制約をに設定しているようです。DEFERRABLE(これはパフォーマンス上の理由によるものだと推測していますが、確かではありません。) Postgres 9.4 の時点で、必要に応じて制約を変更して遅延可能にすることはそれほど難しくありません。

ALTER TABLE table_1 ALTER CONSTRAINT table_1_parent_id_foreign DEFERRABLE;

( http://www.postgresql.org/docs/9.4/static/sql-altertable.htmlを参照してください。)

このアプローチは、一部の人が説明したように制約を削除して再作成するか、トランザクションの終了まですべて (またはすべてのユーザー) トリガーを無効にするよりも望ましいと思います

于 2015-05-05T13:59:54.443 に答える
-3

簡単な解決策は、必要な場所に関連付けられた「一時的な」列を作成することだと思います。

新しい列への外部キーで値を更新します

最初の列を削除します

新しい「一時的な」列の名前を、最初の列と同じ名前に変更します。

于 2011-01-24T09:40:34.790 に答える