如何優雅的ALTER被引用的TABLE
標簽
PostgreSQL , 視圖 , 表 , alter table , 修改字段屬性
背景
在修改數據庫表字段的長度時,數據庫提供了alter table的語法進行修改。
但是被修改的字段如果有其他引用(例如視圖)時,必須先將引用的對象刪除,再修改對應的字段。
例子如下
測試表
postgres=# create table test_t (id int, info text, crt_time timestamp, c1 varchar(10));
CREATE TABLE
被修改字段上建立索引
postgres=# create index idx_test_t on test_t(c1);
CREATE INDEX
被修改字段上建立視圖
postgres=# create view v_test_t as select id,c1 from test_t;
CREATE VIEW
修改字段的長度報錯
postgres=# alter table test_t alter column c1 type varchar(32);
ERROR: cannot alter type of a column used by a view or rule
DETAIL: rule _RETURN on view v_test_t depends on column "c1"
優雅的修改被視圖引用的表字段
PostgreSQL支持將DDL語句封裝在事務中處理,所以從刪除依賴,到修改字段,再到重建依賴,都可以封裝在一個事務中完成。
注意
1. DDL是需要對表加排它鎖的,排它鎖與所有其他鎖衝突,因此建議在事務開始時設置鎖超時參數,避免問題。
2. 如果修改字段涉及到rewrite table(例如int改到text),那麼表很大時間會很久。如果需要很久,意味著需要長時間持有排它鎖(堵塞也是比較嚴重的)。
例子
begin; -- 開始事務
set local lock_timeout = '1s'; -- 設置鎖超時
drop view v_test_t; -- 刪除依賴視圖
alter table test_t alter column c1 type varchar(32); -- 修改字段長度
create view v_test_t as select id,c1 from test_t; -- 創建視圖
end; -- 結束事務
hack PostgreSQL pg_attribute元數據的修改方法
PostgreSQL的定義都記錄在元數據中,所以某些操作,可以直接修改元數據來實現。比如從numeric低精度修改到高精度,從字符串短長度修改到長長度。
注意
不建議這麼做,直接修改元數據存在隱患,甚至可能對數據庫造成不可修複的傷害。
例子
首先要查看將要修改的C1字段的pg_attribute元信息
視圖、索引、表 在pg_attribute中都有對應的元信息,如下
postgres=# select attrelid::regclass,* from pg_attribute where attname='c1';
attrelid | attrelid | attname | atttypid | attstattarget | attlen | attnum | attndims | attcacheoff | atttypmod | attbyval | attstorage | attalign | attnotnull | atthasdef | attidentity | attisdropped | attislocal | attinhcount | attcollation | attacl | attoptions | attfdwoptions
------------+----------+---------+----------+---------------+--------+--------+----------+-------------+-----------+----------+------------+----------+------------+-----------+-------------+--------------+------------+
test_t | 21988 | c1 | 1043 | -1 | -1 | 4 | 0 | -1 | 36 | f | x | i | f | f | | f | t | 0 | 100 | | |
v_test_t | 21998 | c1 | 1043 | -1 | -1 | 2 | 0 | -1 | 36 | f | x | i | f | f | | f | t | 0 | 100 | | |
idx_test_t | 22002 | c1 | 1043 | -1 | -1 | 1 | 0 | -1 | 36 | f | x | i | f | f | | f | t | 0 | 100 | | |
(6 rows)
在修改時,需要將這三個atttypmod一起修改掉。
變長字段的長度為4字節頭+實際長度,所以36表示可以存儲32個字符。
修改為varchar(64)這樣操作
postgres=# update pg_attribute set atttypmod=68 where attname='c1' and attrelid in (21988,21998,22002);
UPDATE 3
更新後,可以看到結構發生了變化.
postgres=# \d+ test_t
Table "public.test_t"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
----------+-----------------------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | | | plain | |
info | text | | | | extended | |
crt_time | timestamp without time zone | | | | plain | |
c1 | character varying(64) | | | | extended | |
Indexes:
"idx_test_t" btree (c1)
postgres=# \d+ v_test_t
View "public.v_test_t"
Column | Type | Collation | Nullable | Default | Storage | Description
--------+-----------------------+-----------+----------+---------+----------+-------------
id | integer | | | | plain |
c1 | character varying(64) | | | | extended |
View definition:
SELECT test_t.id,
test_t.c1
FROM test_t;
postgres=# \d+ idx_test_t
Index "public.idx_test_t"
Column | Type | Definition | Storage
--------+-----------------------+------------+----------
c1 | character varying(64) | c1 | extended
btree, for table "public.test_t"
參考
《PostgreSQL 9.0 modify pg_attribute.atttypmod extend variable char length avoid rewrite table》
《PostgreSQL WHY modify numeric scale must rewrite table》
《PostgreSQL How can i decode the NUMERIC precision and scale in pg_attribute.atttypmod》
最後更新:2017-05-03 21:49:06