Les résultats de Google sur celui-ci sont un peu minces, mais suggèrent que ce n'est pas facilement possible.
Mon problème spécifique est que je dois renuméroter les identifiants dans deux tables liées l'une à l'autre, de manière à ce que la table B contienne une colonne "table_a_id". Je ne peux pas renuméroter la table A d'abord parce qu'alors ses enfants de B pointent vers les anciens ID. Je ne peux pas renuméroter la table B d'abord car ils indiqueraient alors les nouveaux ID avant leur création. Répétez maintenant pour trois ou quatre tables.
Je ne veux pas vraiment avoir à bricoler avec des relations individuelles alors que je pouvais simplement "démarrer une transaction, désactiver l'intégrité de la référence, trier les ID, réactiver l'intégrité de la référence; engager une transaction". Mysql et MSSQL fournissent tous les deux cette fonctionnalité IIRC, donc je serais surpris que Postgres ne le fasse pas.
Merci!
Cela ne semble pas possible. D'autres suggestions font presque toujours référence à la suppression des contraintes et à leur recréation une fois le travail terminé.
Cependant, il semble que vous puissiez créer des contraintes DEFERRABLE
, de sorte qu'elles ne soient pas vérifiées jusqu'à la fin d'une transaction. Voir la documentation PostgreSQL pour CREATE TABLE
(recherchez 'deferrable', il se trouve au milieu de la page).
Vous pouvez faire deux choses (complémentaires, pas alternatives):
J'ai trouvé ces 2 excellents scripts qui génèrent le SQL pour laisser tomber les contraintes et les recréer Les voici:
Pour lâcher les contraintes
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
Pour les recréer
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;
Exécutez ces requêtes et le résultat sera les scripts SQL dont vous avez besoin pour supprimer et créer les contraintes.
Une fois que vous avez supprimé les contraintes, vous pouvez faire tout ce que vous voulez avec les tables. Lorsque vous avez terminé, réintroduisez-les.
Voici un script Python qui supprimera toutes les contraintes d'une transaction, exécutera des requêtes, puis recréera toutes ces contraintes. pg_get_constraintdef
rend cela super facile:
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
Je pense que vous devez dresser une liste de vos contraintes de clé étrangère, les supprimer, faire vos modifications, puis rajouter les contraintes. Consultez la documentation pour alter table drop constraint
et alter table add constraint
.
Si les contraintes sont DEFERRABLE
, c'est vraiment facile. Utilisez simplement un bloc de transaction et définissez vos contraintes FK de manière à les différer au début de la transaction.
De http://www.postgresql.org/docs/9.4/static/sql-set-constraints.html :
SET CONSTRAINTS définit le comportement de la vérification de contrainte dans la transaction en cours. Les contraintes IMMEDIATE sont vérifiées à la fin de chaque instruction. Les contraintes DEFERRED ne sont pas vérifiées jusqu'à la validation de la transaction.
Alors tu pourrais faire:
BEGIN;
SET CONSTRAINTS
table_1_parent_id_foreign,
table_2_parent_id_foreign,
-- etc
DEFERRED;
-- do all your renumbering
COMMIT;
Malheureusement, il semble que Postgres utilise NOT DEFERRABLE
comme valeur par défaut pour toutes les contraintes, à moins que DEFERRABLE
soit explicitement défini. (J'imagine que c'est pour des raisons de performances, mais je ne suis pas certain.) À partir de Postgres 9.4, il n'est pas trop difficile de modifier les contraintes pour les rendre reportables si nécessaire:
ALTER TABLE table_1 ALTER CONSTRAINT table_1_parent_id_foreign DEFERRABLE;
(Voir http://www.postgresql.org/docs/9.4/static/sql-altertable.html .)
Je pense que cette approche serait préférable de supprimer et de recréer vos contraintes comme certains l'ont décrit ou de désactiver tous les déclencheurs (ou tous les utilisateurs) jusqu'à la fin de la transaction, qui nécessite des privilèges de superutilisateur, comme indiqué dans un commentaire précédent comment by @ clapas .