Postgres ALTER TABLE的问题

mar*_*lek 32 postgresql dependencies alter-table

我在postgre中的ALTER TABLE有一个问题.我想改变varchar列的大小.当我尝试这样做时,它表示视图依赖于该列.我不能放弃视图,因为其他东西依赖于它.还有什么方法可以放弃一切并重新创建它吗?

我刚刚找到一个选项,即从视图中删除表连接,当我不更改返回的列时,我可以这样做.但是,我还需要改变更多的观点.有没有什么我怎么能说它应该延迟和提交检查?

Dan*_*que 23

我遇到了这个问题,无法找到任何解决方法.不幸的是,正如我所知,必须删除视图,更改基础表上的列类型,然后重新创建视图.这可以完全在单个事务中发生.

约束延迟不适用于此问题.换句话说,即使SET CONSTRAINTS ALL DEFERRED对此限制也没有影响.具体而言,约束延迟不适用于在ERROR: cannot alter type of a column used by a view or rule尝试更改视图下的列类型时打印的一致性检查.


cra*_*gds 12

如果您不需要更改字段的类型,只需要更改字段的大小,则此方法应该有效:

从这些表开始:

CREATE TABLE foo (id integer primary key, names varchar(10));
CREATE VIEW voo AS (SELECT id, names FROM foo);
Run Code Online (Sandbox Code Playgroud)

\d foo并且\d voo两者都显示长度为10:

id     | integer               | not null
names  | character varying(10) | 
Run Code Online (Sandbox Code Playgroud)

现在将pg_attribute表中的长度更改为20 :

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

(注意:20 + 4是一些疯狂的postgresql遗留物,+ 4是强制性的.)

现在\d foo显示:

id     | integer               | not null
names  | character varying(20) | 
Run Code Online (Sandbox Code Playgroud)

额外奖励:这比以下更快:

ALTER TABLE foo ALTER COLUMN names TYPE varchar(20);
Run Code Online (Sandbox Code Playgroud)

从技术上讲,您可以在不更改视图列大小的情况下更改表列的大小,但不保证会产生哪些副作用; 最好一次改变它们.

来源和更全面的解释:http://sniptools.com/databases/resize-a-column-in-a-postgresql-table-without-changing-data

  • 您应该尽可能避免手动更改目录(例如pg_attribute).在您最不期望的时候,存在出现错误并导致错误(包括崩溃和数据损坏)的真实风险.仅作为最后的手段,在咨询源代码后确保您没有遗漏任何东西.在没有任何免责声明的情况下提出这一建议是不负责任的. (9认同)

Ham*_*one 10

我在派对上有点迟了,但是在这个问题发布多年之后,通过下面引用的一篇文章发布了一个精彩的解决方案(不是我的 - 我只是他的辉煌的感恩受益者).

我刚刚在136个独立视图中引用的对象(在第一级)上测试了这个,并且在其他视图中引用了每个视图.解决方案只需几秒钟.

因此,阅读本文并复制并粘贴表和列出的两个函数:

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

实施示例:

alter table mdm.global_item_master_swap
alter column prod_id type varchar(128),
alter column prod_nme type varchar(512);
Run Code Online (Sandbox Code Playgroud)

错误:无法更改视图或规则使用的列的类型详细信息:视图toolbox_reporting上的规则_RETURN."Average_setcost"取决于列"prod_id"**********错误**********

错误:无法更改视图或规则使用的列的类型

而现在对于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');
Run Code Online (Sandbox Code Playgroud)

- 编辑11/13/2018 -

看来上面的链接可能已经死了.以下是两个过程的代码:

存储DDL的表:

CREATE TABLE util.deps_saved_ddl
(
  deps_id serial NOT NULL,
  deps_view_schema character varying(255),
  deps_view_name character varying(255),
  deps_ddl_to_run text,
  CONSTRAINT deps_saved_ddl_pkey PRIMARY KEY (deps_id)
);
Run Code Online (Sandbox Code Playgroud)

保存和删除:

CREATE OR REPLACE FUNCTION util.deps_save_and_drop_dependencies(
    p_view_schema character varying,
    p_view_name character varying)
  RETURNS void AS
$BODY$
declare
  v_curr record;
begin
for v_curr in 
(
  select obj_schema, obj_name, obj_type from
  (
  with recursive recursive_deps(obj_schema, obj_name, obj_type, depth) as 
  (
    select p_view_schema, p_view_name, null::varchar, 0
    union
    select dep_schema::varchar, dep_name::varchar, dep_type::varchar, recursive_deps.depth + 1 from 
    (
      select ref_nsp.nspname ref_schema, ref_cl.relname ref_name, 
      rwr_cl.relkind dep_type,
      rwr_nsp.nspname dep_schema,
      rwr_cl.relname dep_name
      from pg_depend dep
      join pg_class ref_cl on dep.refobjid = ref_cl.oid
      join pg_namespace ref_nsp on ref_cl.relnamespace = ref_nsp.oid
      join pg_rewrite rwr on dep.objid = rwr.oid
      join pg_class rwr_cl on rwr.ev_class = rwr_cl.oid
      join pg_namespace rwr_nsp on rwr_cl.relnamespace = rwr_nsp.oid
      where dep.deptype = 'n'
      and dep.classid = 'pg_rewrite'::regclass
    ) deps
    join recursive_deps on deps.ref_schema = recursive_deps.obj_schema and deps.ref_name = recursive_deps.obj_name
    where (deps.ref_schema != deps.dep_schema or deps.ref_name != deps.dep_name)
  )
  select obj_schema, obj_name, obj_type, depth
  from recursive_deps 
  where depth > 0
  ) t
  group by obj_schema, obj_name, obj_type
  order by max(depth) desc
) loop

  insert into util.deps_saved_ddl(deps_view_schema, deps_view_name, deps_ddl_to_run)
  select p_view_schema, p_view_name, 'COMMENT ON ' ||
  case
  when c.relkind = 'v' then 'VIEW'
  when c.relkind = 'm' then 'MATERIALIZED VIEW'
  else ''
  end
  || ' ' || n.nspname || '.' || c.relname || ' IS ''' || replace(d.description, '''', '''''') || ''';'
  from pg_class c
  join pg_namespace n on n.oid = c.relnamespace
  join pg_description d on d.objoid = c.oid and d.objsubid = 0
  where n.nspname = v_curr.obj_schema and c.relname = v_curr.obj_name and d.description is not null;

  insert into util.deps_saved_ddl(deps_view_schema, deps_view_name, deps_ddl_to_run)
  select p_view_schema, p_view_name, 'COMMENT ON COLUMN ' || n.nspname || '.' || c.relname || '.' || a.attname || ' IS ''' || replace(d.description, '''', '''''') || ''';'
  from pg_class c
  join pg_attribute a on c.oid = a.attrelid
  join pg_namespace n on n.oid = c.relnamespace
  join pg_description d on d.objoid = c.oid and d.objsubid = a.attnum
  where n.nspname = v_curr.obj_schema and c.relname = v_curr.obj_name and d.description is not null;

  insert into util.deps_saved_ddl(deps_view_schema, deps_view_name, deps_ddl_to_run)
  select p_view_schema, p_view_name, 'GRANT ' || privilege_type || ' ON ' || table_schema || '.' || table_name || ' TO ' || grantee
  from information_schema.role_table_grants
  where table_schema = v_curr.obj_schema and table_name = v_curr.obj_name;

  if v_curr.obj_type = 'v' then
    insert into util.deps_saved_ddl(deps_view_schema, deps_view_name, deps_ddl_to_run)
    select p_view_schema, p_view_name, 'CREATE VIEW ' || v_curr.obj_schema || '.' || v_curr.obj_name || ' AS ' || view_definition
    from information_schema.views
    where table_schema = v_curr.obj_schema and table_name = v_curr.obj_name;
  elsif v_curr.obj_type = 'm' then
    insert into util.deps_saved_ddl(deps_view_schema, deps_view_name, deps_ddl_to_run)
    select p_view_schema, p_view_name, 'CREATE MATERIALIZED VIEW ' || v_curr.obj_schema || '.' || v_curr.obj_name || ' AS ' || definition
    from pg_matviews
    where schemaname = v_curr.obj_schema and matviewname = v_curr.obj_name;
  end if;

  execute 'DROP ' ||
  case 
    when v_curr.obj_type = 'v' then 'VIEW'
    when v_curr.obj_type = 'm' then 'MATERIALIZED VIEW'
  end
  || ' ' || v_curr.obj_schema || '.' || v_curr.obj_name;

end loop;
end;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
Run Code Online (Sandbox Code Playgroud)

恢复:

CREATE OR REPLACE FUNCTION util.deps_restore_dependencies(
    p_view_schema character varying,
    p_view_name character varying)
  RETURNS void AS
$BODY$
declare
  v_curr record;
begin
for v_curr in 
(
  select deps_ddl_to_run 
  from util.deps_saved_ddl
  where deps_view_schema = p_view_schema and deps_view_name = p_view_name
  order by deps_id desc
) loop
  execute v_curr.deps_ddl_to_run;
end loop;
delete from util.deps_saved_ddl
where deps_view_schema = p_view_schema and deps_view_name = p_view_name;
end;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
Run Code Online (Sandbox Code Playgroud)

  • 你是一个伟大的灵魂。 (2认同)
  • 这太棒了——我可以完美地更新具有数百个依赖视图的表。 (2认同)
  • @RajshriMohanKS - 这似乎是 Postgres 12 的问题。我正在调查它,因为我刚刚遇到了同样的错误 (2认同)

ben*_*diy 6

我今天遇到了这个问题并找到了解决方法,以避免丢弃和重新创建VIEW.我不能放弃我的VIEW,因为它是一个主VIEW,它有很多依赖的VIEW.如果没有重建脚本到DROP CASCADE,然后重新创建我的所有VIEW,这是一个解决方法.

我更改了我的主VIEW,为违规列使用了虚拟值,更改了表中的列,并将我的VIEW切换回列.使用这样的设置:

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;
Run Code Online (Sandbox Code Playgroud)

试图像这样改变base_table_field1类型:

ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6);
Run Code Online (Sandbox Code Playgroud)

会给你这个错误:

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"
Run Code Online (Sandbox Code Playgroud)

如果您更改master_view以使用列的虚拟值,如下所示:

CREATE OR REPLACE VIEW master_view AS 
  SELECT
    base_table_id AS id,
    0.9999 AS field1
  FROM base_table;
Run Code Online (Sandbox Code Playgroud)

然后运行你的改变:

ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6);
Run Code Online (Sandbox Code Playgroud)

然后切换回来:

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

这完全取决于您的master_view是否具有不会更改的显式类型.由于我的VIEW使用'(base_table_field1*.01):: numeric AS field1',它可以工作,但'base_table_field1 AS field1'不会,因为列类型会改变.在某些情况下,这种方法可能会有所帮助.

  • 这比删除视图,改变表格和再次创建视图更好吗?我发现这更糟糕,必须查看视图的DDL并查找列的实例.当您丢弃时,您只需要保留原始视图的DDL副本,以便您可以再次创建它. (5认同)
  • 这比删除视图更好吗?第一行......"它是一个主VIEW,它有很多依赖的VIEW." 也就是说,下降也会对这些相关视图进行级联. (4认同)