GaussDB(DWS)應用實戰:對被視圖引用的表進行DDL操作


摘要:GaussDB(DWS)是從Postgres演進過來的,像Postgres一樣,如果表被視圖引用的話,特定場景下,部分DDL操作是不能直接執行的。

背景說明

GaussDB(DWS)是從Postgres演進過來的,像Postgres一樣,如果表被視圖引用的話,特定場景下,部分DDL操作是不能直接執行的,比如修改被視圖引用的字段的類型,刪除表等,而新增字段是可以操作,主要原因是視圖引用了表的字段,修改的話視圖也需要變化。下面稍微演示一下這部分內容,被視圖引用的表進行DDL操作會有什么表現。然后再看看怎么操作才能修改表字段等。

生成實驗內容

建2個測試表,3個測試視圖,建的SQL語句如下,注意所有視圖都是使用了t1的字段,沒有使用t2的字段。

CREATE TABLE t1 (id int,name varchar(20));
CREATE TABLE t2 (id int,name varchar(20));
CREATE OR REPLACE VIEW v1 as select * from t1;
CREATE OR REPLACE VIEW v2 as select a.* from t1 a inner join t2 b on a.id = b.id;
CREATE OR REPLACE VIEW v3 as select a.* from v1 a inner join v2 b on a.id = b.id inner join t1 c on a.id = c.id;

一、刪除表

DROP TABLE t1;
DROP TABLE t2;

從執行結果提示來看,DROP TABLE是沒有執行成功的,因為有視圖依賴。可以通過DROP ...CASCADE來一起將依賴的視圖刪除,但是一般情況下我們不想將視圖刪除。

二、修改字段

ALTER TABLE T1 MODIFY NAME VARCHAR(30);
ALTER TABLE T2 MODIFY NAME VARCHAR(30);

從執行結果的提示來看,t1表修改字段類型失敗了,因為視圖v2使用了這個字段,而t2表修改成功了,因為沒有視圖使用到t2的字段,雖然視圖里面使用了t2表,但只是用來關聯,視圖的字段並沒有使用t2表的字段,所以t2表的字段類型能修改成功。

為了后面實驗能順利實現目標,此處修改v2的視圖,讓其獲取t2的字段

ALTER TABLE T2 MODIFY NAME VARCHAR(20);
CREATE OR REPLACE VIEW v2 as select b.* from t1 a inner join t2 b on a.id = b.id;

三、新增字段

ALTER TABLE t1 ADD COMMENT VARCHAR(30);
ALTER TABLE t2 ADD  COMMENT VARCHAR(30);

新增字段沒有任何限制,因為視圖建立的時候,沒辦法引用還沒有的字段。我們審視視圖的定義CREATE VIEW v1 AS SELECT * FROM t1; 那此時v1會有新增的字段信息嗎?答案是否定的,視圖需要重新刷新才會有新增的字段

select * from v2;
CREATE OR REPLACE VIEW v2 as select a.* from t1 a inner join t2 b on a.id = b.id;
select * from v2;

如何修改被視圖引用的表定義?

那么問題來了,怎么樣才能修改類似上面修改字段的修改被視圖引用的表的定義呢?

我覺得可以分以下幾步

備份視圖定義到文本->備份表定義到文本->文本中修改表定義->備份表(ALTER TABLE XX RENAME TO XX_BAK)->新增修改后的表->插入數據->備份視圖文本刷新視圖

其中比較難獲取的一個內容是,表被哪些視圖引用?這里面需要使用pg_rewrite獲取引用關系,以及with recursive .. as 循環獲取。

一、備份視圖定義到文本

先獲取表設計到哪些視圖,這個SQL稍微有點復雜,這里分幾步來說明

通過pg_rewrite拿到表與視圖的依賴關系

select c.nspname as schemaname,b.relname,rel_oid,b.relkind,d.oid as ori_oid,d.relname ori_name
    from (
    select unnest(regexp_matches(ev_action::text,':relid (\d+)', 'g'))::oid  rel_oid,ev_class --rel_oid 被依賴對象 ,ev_class 視圖名稱
    from pg_rewrite 
    union 
    select unnest(regexp_matches(ev_action::text,':resorigtbl (\d+)','g'))::oid,ev_class
    from pg_rewrite 
     ) deptbl                   --pg_write獲取依賴關系
    inner join pg_class b       --被依賴對象獲取表名等信息
    on deptbl.rel_oid = b.oid
    inner join pg_namespace c
    on b.relnamespace = c.oid
    inner join pg_class d     --視圖獲取視圖名等信息,且用於排除pg_write獲取的自身對象,即rel_oid <> ev_class
    on deptbl.ev_class = d.oid
    and deptbl.rel_oid <> d.oid
    where  b.relname = 't2'; --指定表名t2

通過with recursive xx as循環語句獲取所有相關視圖

with recursive rec_view as (
    select c.nspname as schemaname,b.relname,rel_oid,b.relkind,d.oid as ori_oid,d.relname ori_name
    ,0 as level  --level防止死循環
    from (
    select unnest(regexp_matches(ev_action::text,':relid (\d+)', 'g'))::oid  rel_oid,ev_class --rel_oid 被依賴對象 ,ev_class 視圖名稱
    from pg_rewrite 
    union 
    select unnest(regexp_matches(ev_action::text,':resorigtbl (\d+)','g'))::oid,ev_class
    from pg_rewrite 
     ) deptbl                   --pg_write獲取依賴關系
    inner join pg_class b       --被依賴對象獲取表名等信息
    on deptbl.rel_oid = b.oid
    inner join pg_namespace c
    on b.relnamespace = c.oid
    inner join pg_class d     --視圖獲取視圖名等信息,且用於排除pg_write獲取的自身對象,即rel_oid <> ev_class
    on deptbl.ev_class = d.oid
    and deptbl.rel_oid <> d.oid
    where  b.relname = 't2' --指定表名t2
union all
    select c.nspname,b.relname,deptbl.rel_oid,b.relkind,d.oid as ori_oid,d.relname ori_name,level+1
    from (
    select unnest(regexp_matches(ev_action::text,':relid (\d+)', 'g'))::oid  rel_oid,ev_class
    from pg_rewrite 
  union 
    select unnest(regexp_matches(ev_action::text,':resorigtbl (\d+)','g'))::oid,ev_class
    from pg_rewrite 
  ) deptbl 
    inner join pg_class b
    on deptbl.rel_oid = b.oid
    inner join pg_namespace c
    on b.relnamespace = c.oid
    inner join pg_class d
    on deptbl.ev_class = d.oid
    and deptbl.rel_oid <> d.oid
    inner join rec_view e          --循環語句關聯條件
    on deptbl.rel_oid = e.ori_oid
    where level <=10    --level防止死循環
)
select * from rec_view;

從結果看,t2所以相關視圖是v2,v3兩個視圖。

拿到視圖清單后,我們將v2,v3兩個視圖備份到文本中,使用gs_dump的方式。

gs_dump mydb1 -s -t v2 -t v3 -c -f view.ddl -p 25308

二、備份表定義到文本->文本中修改表定義->備份表(ALTER TABLE XX RENAME TO XX_BAK)->新增修改后的表並插入數據

備份表定義到文本:使用gs_dump將t2的表結構導出到文件

文本中修改表定義:將name的字段類型從原來的varchar(30)修改為varchar(50)

備份表(ALTER TABLE XX RENAME TO XX_BAK):在文本中增加ALTER TABLE RENAME動作

新增修改后的表並插入數據:在文本中增加插入數據SQL

gs_dump mydb1 -s -t t2  -f t2.ddl -p 25308

上述內容修改后,結果如下圖

執行該文本語句

gsql -d mydb1 -p 25308 -r  -f t2.ddl

三、刷新視圖

執行導出的v2,v3視圖

gsql -d mydb1 -p 25308 -r  -f view.ddl

然后檢查t2表是否修改了定義,並查看視圖是否能夠查詢

\d t2
select * from v2;
select * from v3;

總結

因為視圖使用表時會產生依賴關系,在修改被視圖依賴的表的定義時,特定情況下是沒辦法修改的,這里我認為可以通過以下步驟來實現:備份視圖定義到文本->備份表定義到文本->文本中修改表定義->備份表(ALTER TABLE XX RENAME TO XX_BAK)->新增修改后的表->插入數據->備份視圖文本刷新視圖

其中備份視圖定義這一步,需要先知道你需要修改的表的相關視圖是什么。這個查詢的過程需要使用pg_rewrite表和with recursive xx as遞歸獲取相關視圖。獲取到相關視圖備份下來以后,剩下的步驟就比較簡單了。

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM