Postgresql - 更改varchar列的大小

Lab*_*cle 136 postgresql varchar alter-table

ALTER TABLE对一个非常大的表(大约3000万行)上的命令有疑问.其中一个列是a varchar(255),我想将其调整为a varchar(40).基本上,我想通过运行以下命令来更改我的列:

ALTER TABLE mytable ALTER COLUMN mycolumn TYPE varchar(40);
Run Code Online (Sandbox Code Playgroud)

如果进程很长,我没有问题,但似乎我的表在ALTER TABLE命令期间不再可读.有更聪明的方法吗?也许添加一个新列,从旧列复制值,删除旧列,最后重命名新列?

任何线索将不胜感激!提前致谢,

注意:我使用PostgreSQL 9.0.

小智 83

在PostgreSQL 9.1中有一种更简单的方法

http://www.postgresql.org/message-id/162867790801110710g3c686010qcdd852e721e7a559@mail.gmail.com

CREATE TABLE foog(a varchar(10));

ALTER TABLE foog ALTER COLUMN a TYPE varchar(30);

postgres=# \d foog

 Table "public.foog"
 Column |         Type          | Modifiers
--------+-----------------------+-----------
 a      | character varying(30) |
Run Code Online (Sandbox Code Playgroud)

  • 请注意,它只能用于指定**更大的**尺寸(30> 10).如果尺寸较小,您将得到[与我相同的错误](http://stackoverflow.com/a/10991954/1098603). (6认同)
  • 如果您通过 ALTER TABLE 查询降低 varchar 大小,Postgres 应该**不** 抛出错误**除非**多行之一包含超过新大小的值。 (3认同)

Gre*_*ith 69

有关如何在PostgreSQL表调整列大小而不更改数据的说明.您必须破解数据库目录数据.正式执行此操作的唯一方法是使用ALTER TABLE,并且正如您所知,更改将在整个表运行时锁定并重写.

在更改之前,请务必阅读文档的" 字符类型"部分.这里有各种各样奇怪的案例需要注意.当值存储到行中时,将完成长度检查.如果您在那里破解下限,则根本不会减小现有值的大小.您最好在整个表格上进行扫描,以查找在进行更改后字段长度> 40个字符的行.你需要弄清楚如何手动截断它们 - 所以你只是在超大的那些上回来了一些锁 - 因为如果有人试图更新那一行上的任何东西,那么它现在就会拒绝它,因为它太大了它去存储行的新版本.为用户提供欢闹.

VARCHAR是PostgreSQL中存在的一种可怕的类型,只是为了符合SQL标准中相关的可怕部分.如果您不关心多数据库兼容性,请考虑将数据存储为TEXT并添加约束以限制其长度.您可以在没有此表锁定/重写问题的情况下更改约束,并且它们可以执行更多完整性检查,而不仅仅是弱长度检查.

  • 这个链接已经死了. (12认同)
  • 更改varchar长度不会重写表.它只是像CHECK CONSTRAINT一样检查整个表的约束长度.如果你增加长度没有任何关系,只需下一次插入或更新将接受更长的长度.如果减小长度并且所有行都传递新的较小约束,则除了允许下一次插入或更新仅写入新长度之外,Pg不会采取任何进一步操作. (6认同)
  • @bigown,只是为了澄清,你的陈述是[仅适用于PostgreSQL 9.2+](http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.2#Reduce_ALTER_TABLE_rewrites),而不是旧的. (3认同)

Ser*_*gey 45

好吧,我可能迟到了派对,但......

在你的情况下,没有必要调整柱子的大小!

Postgres与其他一些数据库不同,它足够聪明,只能使用足够的空间来容纳字符串(即使使用压缩来处理更长的字符串),所以即使你的列被声明为VARCHAR(255) - 如果你存储了40个字符的字符串在列中,空间使用量将为40字节+ 1字节的开销.

短字符串(最多126个字节)的存储要求是1个字节加上实际字符串,其中包括字符空间填充.较长的字符串有4个字节的开销而不是1.长字符串由系统自动压缩,因此磁盘上的物理要求可能更少.非常长的值也存储在后台表中,因此它们不会干扰对较短列值的快速访问.

(http://www.postgresql.org/docs/9.0/interactive/datatype-character.html)

VARCHAR中的大小规范仅用于检查插入的值的大小,它不会影响磁盘布局.事实上,VARCHAR和TEXT字段以相同的方式存储在Postgres中.

  • 永远不会太晚添加有关"为什么"的更多信息!感谢您提供所有这些信息 (7认同)

Mat*_*ieu 34

我正面临同样的问题,试图将VARCHAR从32截断到8并获得ERROR: value too long for type character varying(8).我希望保持尽可能接近SQL,因为我使用的是自制的JPA结构,我们可能需要根据客户的选择切换到不同的DBMS(PostgreSQL是默认的).因此,我不想使用改变系统表的技巧.

我最后使用了以下USING声明ALTER TABLE:

ALTER TABLE "MY_TABLE" ALTER COLUMN "MyColumn" TYPE varchar(8)
USING substr("MyColumn", 1, 8)
Run Code Online (Sandbox Code Playgroud)

正如@raylu所指出的那样,ALTER获得了对表的独占锁定,因此所有其他操作将被延迟直到完成.

  • ALTER在桌子上获得排他锁,并阻止所有其他操作 (2认同)

jac*_*ade 8

如果将alter放入事务中,则不应锁定表:

BEGIN;
  ALTER TABLE "public"."mytable" ALTER COLUMN "mycolumn" TYPE varchar(40);
COMMIT;
Run Code Online (Sandbox Code Playgroud)

这对我来说非常快速,在一张超过40万行的桌子上几秒钟.

  • 您为什么期望显式事务包装器更改ALTER语句的锁定行为?没有。 (2认同)
  • 你对校长的回答是错误的。任何没有显式事务包装器的 DDL 语句都隐式地在事务中运行。显式事务唯一可能的影响是锁保持*更长* - 直到显式`COMMIT`。仅当您想将更多命令放入同一事务时,包装器才有意义。 (2认同)

Tom*_*Tom 7

这是Greg Smith描述的页面缓存.如果死亡,alter语句如下所示:

UPDATE pg_attribute SET atttypmod = 35+4
WHERE attrelid = 'TABLE1'::regclass
AND attname = 'COL1';
Run Code Online (Sandbox Code Playgroud)

如果您的表是TABLE1,则该列为COL1,您希望将其设置为35个字符(根据链接,遗留目的需要+4,可能是注释中AH引用的开销).


spa*_*ats 7

在redshift postgresql上添加新列并用旧版替换新列,请参阅此链接以获取更多详细信息https://gist.github.com/mmasashi/7107430

BEGIN;
LOCK users;
ALTER TABLE users ADD COLUMN name_new varchar(512) DEFAULT NULL;
UPDATE users SET name_new = name;
ALTER TABLE users DROP name;
ALTER TABLE users RENAME name_new TO name;
END;
Run Code Online (Sandbox Code Playgroud)


小智 5

尝试运行以下更改表:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Run Code Online (Sandbox Code Playgroud)