Google results on this one are a bit thin, but suggest that it is not easily possible.
My specific problem is that I need to renumber the IDs in two tables that are related to each other such that table B has an "table_a_id" column in it. I can't renumber table A first because then its children in B point to the old IDs. I can't renumber table B first because then they would point to the new IDs before they were created. Now repeat for three or four tables.
I don't really want to have to fiddle around with individual relationships when I could just "start transaction; disable ref integrity; sort IDs out; re-enable ref integrity; commit transaction". Mysql and MSSQL both provide this functionality IIRC so I would be surprised if Postgres didn't.
Thanks!
It does not seem possible. Other suggestions almost always refer to dropping the constraints and recreating them after work is done.
However, it seems you can make constraints DEFERRABLE
, such that they are not checked until the end of a transaction. See PostgreSQL documentation for CREATE TABLE
(search for 'deferrable', it's in the middle of the page).
There are two things you can do (these are complementary, not alternatives):
- Create your foreign key constraints as DEFERRABLE. Then, call "SET CONSTRAINTS DEFERRED;", which will cause foreign key constraints not to be checked until the end of the transaction. Note that the default if you don't specify anything is NOT DEFERRABLE (annoyingly).
- Call "ALTER TABLE mytable DISABLE TRIGGER ALL;", which prevents any triggers executing while you load data, then "ALTER TABLE mytable ENABLE TRIGGER ALL;" when you're done to re-enable them.
I found these 2 excellent scripts which generate the sql for dropping the constraints and then recreating them. here they are:
For dropping the constraints
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
For recreating them
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;
Run these queries and the output will be the sql scripts that you need for dropping and creating the constraints.
Once you drop the constraints you can do all you like with the tables. When you are done re-introduce them.
I think you need to make a list of your foreign key constraints, drop them, do your changes, then add the constraints again. Check the documentation for alter table drop constraint
and alter table add constraint
.
Here's a Python script that will delete all constraints in a transaction, run some queries, then recreate all those constraints. pg_get_constraintdef
makes this super-easy:
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
If the constraints are DEFERRABLE
, this is really easy. Just use a transaction block and set your FK constraints to be deferred at the beginning of the transaction.
From http://www.postgresql.org/docs/9.4/static/sql-set-constraints.html:
SET CONSTRAINTS sets the behavior of constraint checking within the current transaction. IMMEDIATE constraints are checked at the end of each statement. DEFERRED constraints are not checked until transaction commit.
So you could do:
BEGIN;
SET CONSTRAINTS
table_1_parent_id_foreign,
table_2_parent_id_foreign,
-- etc
DEFERRED;
-- do all your renumbering
COMMIT;
Unfortunately, it seems Postgres defaults all constraints to NOT DEFERRABLE
, unless DEFERRABLE
is explicitly set. (I'm guessing this is for performance reasons, but I'm not certain.) As of Postgres 9.4, it isn't too hard to alter the constraints to make them deferrable if needed:
ALTER TABLE table_1 ALTER CONSTRAINT table_1_parent_id_foreign DEFERRABLE;
(See http://www.postgresql.org/docs/9.4/static/sql-altertable.html.)
I think this approach would be preferable to dropping and recreating your constraints as some have described, or to disabling all (or all user) triggers until the end of the transaction, which requires superuser privileges, as noted in an earlier comment by @clapas.
I think that an easear solution would be to create "temporary" columns associating where you want them to be.
update the values with the foreign keys to the new columns
drop the inicial columns
rename to the new "temporary" columns to the same names then the inicial ones.
来源:https://stackoverflow.com/questions/139884/how-do-i-disable-referential-integrity-in-postgres-8-2