これに関するグーグルの結果は少し薄いですが、それが簡単に可能ではないことを示唆しています。
私の特定の問題は、テーブルBに「table_a_id」列が含まれるように、相互に関連付けられている2つのテーブルのIDを再番号付けする必要があることです。最初にテーブルAの番号を付け直すことはできません。それは、Bの子が古いIDを指しているためです。最初にテーブルBの番号を付け直すことはできません。これは、テーブルBが作成される前に新しいIDをポイントするためです。 3つまたは4つのテーブルについて繰り返します。
「トランザクションを開始する、参照整合性を無効にする、IDを並べ替える、参照整合性を再度有効にする、トランザクションをコミットする」だけの場合は、個々の関係をいじる必要はありません。 MysqlとMSSQLの両方がこの機能IIRCを提供しているので、Postgresが提供していなかったとしたら驚きます。
ありがとう!
それは可能ではないようです。他の提案では、ほとんどの場合、制約を削除し、作業が終わった後にそれらを再作成することを指します。
ただし、トランザクションの終了までチェックされないように、制約をDEFERRABLE
に設定できるようです。 CREATE TABLE
)のPostgreSQLドキュメントを参照 (「延期可能」を検索してください。ページの中央にあります)。
できることは2つあります(これらは代替ではなく補完的なものです)。
制約を削除して再作成するための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スクリプトになります。
制約を削除すると、テーブルを好きなように使用できます。完了したら、それらを再導入します。
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
私はあなたがあなたの外部キー制約のリストを作成し、それらを削除し、変更を行い、そして制約を再度追加する必要があると思います。 alter table drop constraint
およびalter table add constraint
のドキュメントを確認してください。
制約が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 によるコメント。