Postgresql-将varchar列的大小更改为较短的长度

问题描述 投票:137回答:8

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

ALTER TABLE mytable ALTER COLUMN mycolumn TYPE varchar(40);

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

任何线索将不胜感激!预先感谢,

注意:我使用PostgreSQL 9.0。

postgresql varchar alter-table
8个回答
69
投票

Resize a column in a PostgreSQL table without changing data上有关于如何执行此操作的说明。您必须修改数据库目录数据。正式执行此操作的唯一方法是使用ALTER TABLE,并且您已经注意到,更改将在运行时锁定并重写整个表。

请确保在更改此内容之前先阅读文档的Character Types部分。这里要注意各种奇怪的情况。将值存储到行中时,将进行长度检查。如果您在其中限制了一个较低的限制,那根本不会减少现有值的大小。进行更改后,最好对整个表进行扫描以查找字段长度大于40个字符的行。您需要弄清楚如何手动截断这些锁-这样您就可以锁住一些超大的锁-因为如果有人尝试更新该行上的任何内容,那么此时它将拒绝它太大它会存储该行的新版本。随之而来的是用户狂欢。

VARCHAR是一个可怕的类型,仅在PostgreSQL中存在,只是为了遵守其与SQL标准相关的可怕部分。如果您不关心多数据库兼容性,请考虑将数据存储为TEXT并添加约束以限制其长度。您可以更改约束而不会出现此表锁定/重写问题,并且约束可以执行更多的完整性检查,而不仅仅是弱长度检查。


87
投票

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

http://www.postgresql.org/message-id/[email protected]

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) |

45
投票

好吧,我可能晚会晚了,但是...

不需要在您的情况下调整列的大小!

Postgres,与某些其他数据库不同,它非常聪明,仅使用足够的空间来容纳字符串(甚至对更长的字符串使用压缩),因此即使您的列声明为VARCHAR(255)-如果您存储40个字符列中的字符串,空间使用量将为40字节+ 1字节的开销。

短字符串(最多126个字节)的存储要求是1个字节加上实际的字符串,其中包括空格性格。较长的字符串的开销为4个字节,而不是1个字节。长字符串由系统自动压缩,因此磁盘上的物理需求可能更少。也很长的值存储在后台表中,以免干扰快速访问较短的列值。

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

VARCHAR中的大小规范仅用于检查所插入值的大小,它不会影响磁盘布局。实际上,VARCHAR and TEXT fields are stored in the same way in Postgres


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)

正如@raylu所指出的,ALTER在表上获得排他锁,因此所有其他操作将延迟到完成为止。


8
投票

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

BEGIN;
  ALTER TABLE "public"."mytable" ALTER COLUMN "mycolumn" TYPE varchar(40);
COMMIT;

这对我来说非常有用,在具有超过40万行的表上快速燃烧了几秒钟。


7
投票

这里是格雷格·史密斯(Greg Smith)描述的页面的the cache。如果也死了,则alter语句如下所示:

UPDATE pg_attribute SET atttypmod = 35+4
WHERE attrelid = 'TABLE1'::regclass
AND attname = 'COL1';

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


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;

1
投票

我发现了一种非常简单的更改大小的方法,即@Size(min = 1,max = 50)批注,它是“ import javax.validation.constraints”的一部分,即“导入javax.validation.constraints.Size;”

@Size(min = 1, max = 50)
private String country;


when executing  this is hibernate you get in pgAdmin III 


CREATE TABLE address
(
.....
  country character varying(50),

.....

)
© www.soinside.com 2019 - 2024. All rights reserved.