2010-07-14 8 views
23

J'ai un problème avec ALTER TABLE dans postgre. Je veux changer la taille de la colonne varchar. Quand j'essaye de faire ceci, il indique que la vue dépend de cette colonne. Je ne peux pas laisser tomber la vue parce que quelque chose d'autre en dépend. Y a-t-il un autre moyen que de tout laisser tomber et de le recréer à nouveau?Problème avec Postgres ALTER TABLE

Je viens de trouver une option, qui est de supprimer la table se joindre à la vue, quand je ne vais pas changer les colonnes retournées, je peux le faire. Mais encore, il y a plus de points de vue que je vais devoir changer. N'y a-t-il rien, comment puis-je dire qu'il devrait être différé et vérifié avec commit?

+1

Copiez la requête d'affichage et supprimez-la pour apporter des modifications à la table. – TaherT

Répondre

15

J'ai rencontré ce problème et je n'ai trouvé aucun moyen de le contourner. Malheureusement, le mieux que je puisse dire, il faut laisser tomber les vues, modifier le type de colonne sur la table sous-jacente, puis recréer les vues. Cela peut se produire entièrement en une seule transaction.

Le report de contrainte ne s'applique pas à ce problème. En d'autres termes, même SET CONSTRAINTS ALL DEFERRED n'a aucun impact sur cette limitation. Pour être précis, le report de contrainte ne s'applique pas à la vérification de cohérence qui imprime ERROR: cannot alter type of a column used by a view or rule lorsque l'on essaie de modifier le type d'une colonne sous-jacente à une vue.

4

J'ai rencontré ce problème aujourd'hui et j'ai trouvé un moyen de contourner le problème pour éviter de laisser tomber et de recréer la vue. Je ne peux pas simplement abandonner ma VUE parce que c'est une VUE principale qui a de nombreuses VUES dépendantes construites dessus. Court d'avoir un script de reconstruction à DROP CASCADE, puis recréer toutes mes VIEWs c'est un travail autour.

Je modifiais ma vue principale pour utiliser une valeur fictive pour la colonne incriminée, modifiais la colonne dans la table et inversions ma vue sur la colonne. En utilisant une configuration comme ceci:

CREATE TABLE base_table 
(
    base_table_id integer, 
    base_table_field1 numeric(10,4) 
); 

CREATE OR REPLACE VIEW master_view AS 
    SELECT 
    base_table_id AS id, 
    (base_table_field1 * .01)::numeric AS field1 
    FROM base_table; 

CREATE OR REPLACE VIEW dependent_view AS 
    SELECT 
    id AS dependent_id, 
    field1 AS dependent_field1 
    FROM master_view; 

Essayer de modifier le type de base_table_field1 comme ceci:

ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6); 

vous donnera cette erreur:

ERROR: cannot alter type of a column used by a view or rule 
DETAIL: rule _RETURN on view master_view depends on column "base_table_field1" 

Si vous changez master_view d'utiliser une valeur factice pour la colonne comme ceci:

CREATE OR REPLACE VIEW master_view AS 
    SELECT 
    base_table_id AS id, 
    0.9999 AS field1 
    FROM base_table; 

votre alter Lancez ensuite:

ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6); 

et faire passer votre vue arrière:

CREATE OR REPLACE VIEW master_view AS 
    SELECT 
    base_table_id AS id, 
    (base_table_field1 * .01)::numeric AS field1 
    FROM base_table; 

Tout dépend si votre master_view a un type explicite qui ne change pas. Puisque mon VIEW utilise '(base_table_field1 * .01) :: numeric AS field1' cela fonctionne, mais 'base_table_field1 AS field1' ne le serait pas parce que le type de colonne change. Cette approche pourrait aider dans certains cas comme le mien.

+4

En quoi cela est-il préférable à la suppression de la vue, à la modification de la table et à la création de la vue? Je trouve que c'est pire, de devoir regarder à travers le DDL de la vue et trouver des instances de la colonne. Lorsque vous déposez, tout ce dont vous avez besoin est de conserver une copie du DDL de la vue d'origine, afin de pouvoir le créer à nouveau. – ADTC

+3

Comment est-ce mieux que de laisser tomber la vue? La première ligne ... "c'est une vue principale qui a de nombreuses VUES dépendantes construites dessus." C'est-à-dire, la chute cascades à ces vues dépendantes ainsi. –

7

Si vous n'avez pas besoin de changer le type du champ, mais juste la taille de celui-ci, cette approche devrait fonctionner:

A partir de ces tables:

CREATE TABLE foo (id integer primary key, names varchar(10)); 
CREATE VIEW voo AS (SELECT id, names FROM foo); 

\d foo et \d voo les deux montrent la longueur 10:

id  | integer    | not null 
names | character varying(10) | 

changer maintenant les longueurs à 20 dans le pg_attribute table:

UPDATE pg_attribute SET atttypmod = 20+4 
WHERE attrelid IN ('foo'::regclass, 'voo'::regclass) 
AND attname = 'names'; 

(note: 20 + 4 est quelque chose folle héritage postgresql, le 4 est obligatoire.)

Maintenant \d foo montre:

id  | integer    | not null 
names | character varying(10) | 

Bonus: c'était waaay plus rapide que de faire:

ALTER TABLE foo ALTER COLUMN names TYPE varchar(20); 

Techniquement, vous pouvez changer la taille de la table c olumn sans changer la taille de la colonne de vue, mais aucune garantie sur les effets secondaires qui auront; il est probablement préférable de les changer tous les deux à la fois.

source de

et une explication plus complète: http://sniptools.com/databases/resize-a-column-in-a-postgresql-table-without-changing-data

+7

Vous devez éviter de modifier manuellement les catalogues (tels que pg_attribute) autant que possible. Il y a un risque réel de se tromper et de provoquer des erreurs, y compris des plantages et la corruption de données, plus tard, quand vous vous y attendez le moins. FAITES-le SEULEMENT en dernier recours, APRÈS avoir consulté le code source pour vous assurer de ne rien manquer.Suggérer cela sans aucun avertissement est irresponsable. – intgr

+0

C'est comme ça que le moteur de données fonctionne vraiment dans les coulisses. Vous ne modifiez pas TYPE, mais la taille. Comme il s'agit de VARCHAR, aucun dommage n'est causé aux données. +1 –

2

Je suis un peu en retard à la fête, mais après des années cette question a été publié, a été posté une solution brillante par un article référencé ci-dessous (pas le mien - je suis simplement un bénéficiaire reconnaissant de son éclat).

Je viens de tester ceci sur un objet référencé (au premier niveau) dans 136 vues distinctes, et chacune de ces vues est référencée dans d'autres vues. La solution a fonctionné en quelques secondes.

Alors, lisez cet article et copier-coller la table et deux fonctions énumérées:

http://mwenus.blogspot.com/2014/04/postgresql-how-to-handle-table-and-view.html

exemple de mise en œuvre:

alter table mdm.global_item_master_swap 
alter column prod_id type varchar(128), 
alter column prod_nme type varchar(512); 

ERROR: cannot alter type of a column used by a view or rule DETAIL: rule _RETURN on view toolbox_reporting."Average_setcost" depends on column "prod_id" ********** Error **********

ERROR: cannot alter type of a column used by a view or rule

Et maintenant la magie de ninja PostgreSQL:

select util.deps_save_and_drop_dependencies('mdm', 'global_item_master_swap'); 


alter table mdm.global_item_master_swap 
alter column prod_id type varchar(128), 
alter column prod_nme type varchar(512); 


select util.deps_restore_dependencies('mdm', 'global_item_master_swap'); 
+0

Vous êtes une grande âme. – Vishnu

Questions connexes