Postgresql

更改 varchar() 的長度限制(類型修飾符)會導致表或索引重寫嗎?

  • November 1, 2017

這個問題之後,更改結果的長度限制(類型修飾符)是否會varchar()導致表重寫或鎖定比更改CHECK約束花費更多時間?從那個問題你可以看到Brandur 的說法

如果您想更改長度,則ALTER TABLE需要獨占鎖(請參閱https://www.postgresql.org/docs/current/static/sql-altertable.html …)。改變CHECK是瞬間的。當回答一個引起問題文本的問題CHECK (char_length(email) <= 255)varchar(255)

看來這可能源於 Depesz 在他 2010 年的文章中的一篇文章,“CHAR(X) VS. VARCHAR(X) VS. VARCHAR VS. TEXT – 2010-03-03 更新”

那麼,當你把限制變大時會發生什麼

$$ with varchar $$? PostgreSQL 必須重寫表。這有 2 個非常重要的缺點: 1. 操作時需要對錶進行排他鎖 2. 對於非平凡的表,將花費相當多的時間

您可以在此處(2017)的評論中再次看到這一點,

儘管如此,當我想將 my 轉換VARCHAR(50)VARCHAR(250).

再次在這裡(2012)

順便說一句:如果可以避免的話,我從不使用 varchar - 特別是不使用長度修飾符。它幾乎沒有提供文本無法做到的任何東西。如果我需要長度限制,我會使用一個列約束,它可以在不重寫整個表的情況下進行更改。

顯然還有其他人對此說法表示懷疑。

僅僅解決這個單一的主張可能是值得的。

本質上,這是過時的資訊。自 9.2 以來,它一直不相關。現在,我可以看到的唯一缺點是,如果長度約束變得更加嚴格並且必須重新檢查,索引會被重寫。

在 9.1 的發行說明中,正如 Erwin 發現的那樣

> > 允許ALTER TABLE ... SET DATA TYPE在適當的情況下避免表重寫 (Noah Misch, Robert Haas) > > > 例如,將 varchar 列轉換為文本不再需要重寫表。但是,增加對 varchar 列的長度約束仍然需要重寫表。 > > >

在 9.2 的發行說明中,由 @a_horse_with_no_name found 發現,

減少為某些ALTER TABLE ... ALTER COLUMN TYPE操作重建表和索引的需要 (Noah Misch)

增加 varchar 或 varbit 列的長度限制,或完全刪除限制,不再需要重寫表。同樣,增加數值列的允許精度,或將列從受約束的數值更改為不受約束的數值,不再需要重寫表。在涉及間隔、時間戳和時間戳記類型的類似情況下,也可以避免表重寫。

上的文件ALTER有這個說法

添加帶有DEFAULT子句的列或更改現有列的類型將需要重寫整個表及其索引。**作為更改現有列的類型時的例外,如果USING子句不更改列內容並且舊類型是二進制可強制轉換為新類型或新類型上的不受約束的域,則不需要重寫表;但仍必須重建受影響列上的任何索引。**添加或刪除系統 oid 列也需要重寫整個表。對於大型表,表和/或索引重建可能需要大量時間;並且將暫時需要兩倍的磁碟空間。

跟著我修改的 a_horse_with_no_name 提供的測試案例,讓我們看看它的實際效果。

\timing 1

CREATE TABLE alter_test (id int primary key, some_data varchar(50));
INSERT INTO alter_test
 SELECT i, md5(i::text)
 FROM generate_series(1,1e7) AS gs(i);

ALTER TABLE alter_test
 ALTER COLUMN some_data
 TYPE varchar(55);
Time: 5.671 ms

因此,如果沒有索引,我們就不會減速。然後我們添加一個索引,再試一次,

CREATE INDEX ON alter_test (some_data);
ALTER TABLE alter_test
 ALTER COLUMN some_data
 TYPE varchar(55);
Time: 6.423 ms

VACUUM FULL ANALYZE我在桌子上試了試,varchar又把長度拉長了,還是沒再花時間。並不是說它在所有情況下都被揭穿了,但至少在簡單的情況下,即使被索引,如果你要減少約束的限制,這似乎不是一個問題。但是,使長度約束更具限制性似乎正在做一些事情。

ALTER TABLE alter_test
 ALTER COLUMN some_data
 TYPE varchar(50);
ALTER TABLE
Time: 59690.885 ms

刪除索引並再次嘗試會更快,

DROP INDEX alter_test_some_data_idx ;
DROP INDEX
Time: 85.978 ms
test=# ALTER TABLE alter_test
 ALTER COLUMN some_data
 TYPE varchar(49);
ALTER TABLE
Time: 9297.271 ms

因此,似乎只有當長度約束變得更加嚴格並且必須重新驗證時,索引才會被重寫。

引用自:https://dba.stackexchange.com/questions/189890