关于Postgresql:Postgresql – 更改varchar列的大小

Postgresql - change the size of a varchar column

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

1
ALTER TABLE mytable ALTER COLUMN mycolumn TYPE VARCHAR(40);

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

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

注意:我使用PostgreSQL 9.0。


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

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

1
2
3
4
5
6
7
8
9
10
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) |


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

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

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


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

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

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

The storage requirement for a short string (up to 126 bytes) is 1 byte
plus the actual string, which includes the space padding in the case
of character. Longer strings have 4 bytes of overhead instead of 1.
Long strings are compressed by the system automatically, so the
physical requirement on disk might be less. Very long values are also
stored in background tables so that they do not interfere with rapid
access to shorter column values.

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

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


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

我在ALTER TABLE中使用USING语句结束了:

1
2
ALTER TABLE"MY_TABLE" ALTER COLUMN"MyColumn" TYPE VARCHAR(8)
USING substr("MyColumn", 1, 8)

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


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

1
2
3
BEGIN;
  ALTER TABLE"public"."mytable" ALTER COLUMN"mycolumn" TYPE VARCHAR(40);
COMMIT;

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


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

1
2
3
4
5
6
7
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;


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

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

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


我找到了一种非常简单的方法来改变大小,即注释@Size(min = 1,max = 50),它是"import javax.validation.constraints"的一部分,即
"import javax.validation.constraints.Size;"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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),

.....

)