Comment puis-je désactiver l'intégrité référentielle dans Postgresql 8.2?
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 ID dans deux tables qui sont liées entre elles de sorte que la table B a une colonne" table_a_id " en elle. Je ne peux pas renuméroter la table A d'abord parce que ses enfants en B pointent vers les vieilles cartes D'identité. Je ne peux pas renuméroter la table B d'abord parce qu'ensuite ils pointeraient vers les nouveaux ID avant qu'ils ne soient créés. Maintenant, répétez l'opération pour les trois ou quatre table.
Je ne veux pas vraiment avoir à tripoter avec les relations individuelles quand je pourrais juste "démarrer la transaction; désactiver l'intégrité ref; trier les IDs; réactiver l'intégrité ref; commit transaction". Mysql et MSSQL fournissent toutes deux cette fonctionnalité IIRC donc je serais surpris que Postgres ne le fasse pas.
Merci!
7 réponses
cela ne semble pas possible. D'autres suggestions consistent presque toujours à supprimer les contraintes et à les recréer une fois le travail terminé.
cependant, il semble que vous pouvez faire des contraintes DEFERRABLE
, de sorte qu'ils ne sont pas vérifiés jusqu'à la fin d'une transaction. Voir documentation PostgreSQL pour CREATE TABLE
(rechercher "déférable", c'est au milieu de la page).
il y a deux choses que vous pouvez faire (ce sont des solutions complémentaires, pas alternatives):
- Créez vos contraintes de clé étrangère comme DÉFÉRABLES. Ensuite, appelez " ensemble de contraintes différé;", ce qui fera que les contraintes de clé étrangère ne seront pas vérifiées avant la fin de la transaction. Notez que la valeur par défaut si vous ne spécifiez rien n'est pas DÉFÉRABLE (ce qui est fâcheux).
- Call " ALTER TABLE mytable DISABLE TRIGGER ALL;", qui empêche toute l'exécution des triggers pendant que vous chargez des données, puis "ALTER TABLE mytable ENABLE TRIGGER ALL;" lorsque vous avez terminé de les réactiver.
j'ai trouvé ces 2 excellents scripts qui génèrent le sql pour supprimer les contraintes et ensuite les recréer. les voici:
de l'abandon De la 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;
Lancez ces requêtes et la sortie sera les scripts sql dont vous avez besoin pour supprimer et créer les contraintes.
une fois que vous laissez tomber les contraintes, vous pouvez faire tout ce que vous voulez avec les tables. Lorsque vous avez terminé de les réintroduire.
je pense que vous avez besoin de faire une liste de vos contraintes clés étrangères, les laisser tomber, faire vos modifications, puis Ajouter les contraintes à nouveau. Consultez la documentation pour alter table drop constraint
et alter table add constraint
.
voici un script Python qui supprimera toutes les contraintes d'une transaction, lancera quelques requêtes, puis recréera toutes ces contraintes. pg_get_constraintdef
rend ce 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 __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
Si les contraintes sont DEFERRABLE
, c'est vraiment facile. Il suffit d'utiliser un bloc de transactions et de définir vos contraintes FK à reporter au début de la transaction.
de http://www.postgresql.org/docs/9.4/static/sql-set-constraints.html :
de DÉFINIR des CONTRAINTES définit le comportement de la vérification des contraintes à l'intérieur de la transaction en cours. Les contraintes immédiates sont vérifiées à la fin de chaque déclaration. Les contraintes différées ne sont pas vérifiées tant que la transaction ne s'est pas engagée.
pour que tu puisses 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 renvoie par défaut toutes les contraintes à NOT DEFERRABLE
, à moins que DEFERRABLE
ne soit explicitement défini. (Je suppose que c'est pour des raisons de performance, mais je ne suis pas sûr.) À partir de Postgres 9.4, il n'est pas trop difficile de modifier les contraintes pour les faire différer 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 à l'abandon et à la recréation de vos contraintes comme certains l'ont décrit, ou à la désactivation de tous les déclencheurs (ou de tous les utilisateurs) jusqu'à la fin de la transaction, ce qui nécessite des privilèges de superutilisateur, comme indiqué dans un précédent commentaire de @clapas .
je pense qu'une solution d'easear serait de créer des colonnes" temporaires " associant où vous voulez qu'elles soient.
mettre à jour les valeurs avec les touches étrangères des nouvelles colonnes
drop the inicial columns
renommer le nouveau "temporaire" des colonnes de même nom, la inicial.