web-dev-qa-db-ja.com

Postgres 8.2で参照整合性を無効にするにはどうすればよいですか?

これに関するグーグルの結果は少し薄いですが、それが簡単に可能ではないことを示唆しています。

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

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

ありがとう!

35
sanbikinoraion

それは可能ではないようです。他の提案では、ほとんどの場合、制約を削除し、作業が終わった後にそれらを再作成することを指します。

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

17
Joel B Fant

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

  • 外部キー制約をDEFERRABLEとして作成します。次に、「SET CONSTRAINTS DEFERRED;」を呼び出します。これにより、トランザクションの終了まで外部キー制約がチェックされなくなります。何も指定しない場合のデフォルトは、(煩わしいことに)DEFERRABLEではないことに注意してください。
  • 「ALTER TABLE mytable DISABLE TRIGGER ALL;」を呼び出して、データの読み込み中にトリガーが実行されないようにしてから、「ALTER TABLE mytable ENABLE TRIGGER ALL;」を呼び出します。あなたがそれらを再び有効にするために完了したとき。
48
Nick Johnson

制約を削除して再作成するための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スクリプトになります。

制約を削除すると、テーブルを好きなように使用できます。完了したら、それらを再導入します。

33
Dimitris

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 __== '__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
5
zzzeek

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

5
Liam

制約が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;

残念ながら、DEFERRABLEが明示的に設定されていない限り、Postgresはすべての制約をNOT 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 を参照してください。)

前述のように、スーパーユーザー権限を必要とするトランザクションの終了まで、一部のユーザーが説明したように制約を削除して再作成するか、すべての(またはすべてのユーザー)トリガーを無効にするよりも、このアプローチが望ましい @ clapas によるコメント。

0
Sean the Bean