我在PostgreSQL中使用ALTER TABLE时遇到了问题。我想要改变一个varchar列的大小,但是当我尝试这样做时,它显示这个视图依赖于那一列,因此我无法删除该视图,因为它会影响其他内容。有没有办法除了删除所有内容并重新创建以外?
我找到了一种解决方案,即从视图中删除表格连接,只要不更改返回的列,我就可以这样做。但仍然有更多的视图需要更改。是否有任何方法可以说应该延迟检查,并在提交时进行检查?
我在PostgreSQL中使用ALTER TABLE时遇到了问题。我想要改变一个varchar列的大小,但是当我尝试这样做时,它显示这个视图依赖于那一列,因此我无法删除该视图,因为它会影响其他内容。有没有办法除了删除所有内容并重新创建以外?
我找到了一种解决方案,即从视图中删除表格连接,只要不更改返回的列,我就可以这样做。但仍然有更多的视图需要更改。是否有任何方法可以说应该延迟检查,并在提交时进行检查?
我遇到了这个问题,但找不到解决方法。据我所知,唯一的方法是删除视图,修改底层表的列类型,然后重新创建视图。这可以在一个事务中完成。
约束延迟不适用于此问题。换句话说,即使使用SET CONSTRAINTS ALL DEFERRED
也无法解决这个限制。具体来说,约束延迟不适用于一致性检查,当试图更改视图下面的列的类型时,会打印出ERROR: cannot alter type of a column used by a view or rule
。
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);
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');
-- 编辑 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)
);
-- 编辑 2020年8月28日 -- -- 在Pg12版本中,此功能停止工作。修复方法如下,将p_view_schema和p_view_name的参数从varchar更改为name:
CREATE OR REPLACE FUNCTION util.deps_save_and_drop_dependencies(
p_view_schema name, p_view_name name)
RETURNS void
LANGUAGE plpgsql
COST 100
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$
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;
declare
v_curr record;
owner_id varchar; -- add this
select viewowner
into owner_id
from pg_catalog.pg_views v
where
v.schemaname = v_curr.obj_schema and
v.viewname = 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, 'ALTER VIEW OWNER TO ' || owner_id
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, 'ALTER MATERIALIZED VIEW OWNER TO ' || owner_id
from pg_matviews
where schemaname = v_curr.obj_schema and matviewname = v_curr.obj_name;
end if;
-- code below already exists (for reference point)
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;
select p_view_schema, p_view_name, 'GRANT ' || privilege_type || ' ON ' || table_schema || '.' || table_name || ' TO ' || grantee
,但仍在使用它。更改后的代码为:select p_view_schema, p_view_name, 'GRANT ' || privilege_type || ' ON ' || table_schema || '.' || table_name || ' TO "' || grantee || '"'
,因为我的用户名中有一个句点。您仍然可以通过psql用户名进行SQL注入,因此请谨慎使用此脚本! - Joshua Klein如果您不需要更改字段的类型,只需更改其大小,则可以尝试以下方法:
从以下表格开始:
CREATE TABLE foo (id integer primary key, names varchar(10));
CREATE VIEW voo AS (SELECT id, names FROM foo);
\d foo
和 \d voo
都显示长度为10:
id | integer | not null
names | character varying(10) |
现在在pg_attribute
表中将长度更改为20:
UPDATE pg_attribute SET atttypmod = 20+4
WHERE attrelid IN ('foo'::regclass, 'voo'::regclass)
AND attname = 'names';
注意:20+4是一些疯狂的postgresql遗留问题,+4是强制性的。\d foo
显示:id | integer | not null
names | character varying(20) |
额外奖励:这比执行以下操作要快得多:
ALTER TABLE foo ALTER COLUMN names TYPE varchar(20);
从技术上讲,您可以更改表列的大小而不更改视图列的大小,但是不能保证这样做不会产生任何副作用;最好同时更改它们两个。
来源和详细说明:http://sniptools.com/databases/resize-a-column-in-a-postgresql-table-without-changing-data
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;
ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6);
会出现以下错误:
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"
CREATE OR REPLACE VIEW master_view AS
SELECT
base_table_id AS id,
0.9999 AS field1
FROM base_table;
然后运行您的修改:
ALTER TABLE base_table ALTER COLUMN base_table_field1 TYPE numeric(10,6);
并切换回您的视图:
CREATE OR REPLACE VIEW master_view AS
SELECT
base_table_id AS id,
(base_table_field1 * .01)::numeric AS field1
FROM base_table;
这完全取决于您的master_view是否具有不变的显式类型。由于我的VIEW使用了'(base_table_field1 * .01)::numeric AS field1',所以它可以工作,但是'base_table_field1 AS field1'则不行,因为列类型会发生变化。这种方法可能在某些情况下有所帮助。
作者:Mateusz Wenus于19:32
do $$
declare gorev_lisans_ihlali_def text;
declare exec_text text;
begin
gorev_lisans_ihlali_def := pg_get_viewdef('public.gorev_lisans_ihlali');
drop view public.gorev_lisans_ihlali;
exec_text := format('create view public.gorev_lisans_ihlali as %s',
gorev_lisans_ihlali_def);
ALTER TABLE public.ara_bakis_duyma
ALTER COLUMN gain TYPE DOUBLE PRECISION;
execute exec_text;
end $$;
EXISTS ( SELECT * FROM ...
的视图会在exists子查询中涉及的所有表的所有列上引入非常不必要的依赖关系,因此创建带有EXISTS (SELECT 1 FROM ...
的视图可能是一个更好的选择。 - Ezequiel Tolnay