Postgresql - 更改varchar列的大小

时间:2011-10-11 16:22:48

标签: postgresql varchar alter-table

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

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

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

任何线索都将非常感谢! 提前谢谢,

注意:我使用PostgreSQL 9.0。

9 个答案:

答案 0 :(得分:75)

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

答案 1 :(得分:64)

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

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

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

答案 2 :(得分:43)

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

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

与其他一些数据库不同,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

答案 3 :(得分:30)

我在尝试将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获取了对表的独占锁定,因此所有其他操作将被延迟直到完成。

答案 4 :(得分:7)

这是Greg Smith描述的页面的the cache。如果同时死亡,则alter语句如下所示:

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

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

答案 5 :(得分:6)

在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;

答案 6 :(得分:6)

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

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

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

答案 7 :(得分: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),

.....

)

答案 8 :(得分:0)

尝试运行以下alter table:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;