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

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

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

注意:我使用PostgreSQL 9.0。


在不改变数据的情况下,在PostgreSQL表中调整列的大小有一个描述。你得破解数据库目录数据。正式做到这一点的唯一方法是使用ALTER TABLE,正如您所注意到的,change将在运行时锁定并重写整个表。

Make sure you read the Character Types section of the docs before changing this. All sorts of weird cases to be aware of here. The length check is done when values are stored into the rows. If you hack a lower limit in there, that will not reduce the size of existing values at all. You would be wise to do a scan over the whole table looking for rows where the length of the field is >40 characters after making the change. You'll need to figure out how to truncate those manually--so you're back some locks just on oversize ones--because if someone tries to update anything on that row it's going to reject it as too big now, at the point it goes to store the new version of the row. Hilarity ensues for the user.

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


下面是Greg Smith描述的页面缓存。如果它也死了,alter语句看起来像这样:

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

您的表是TABLE1,列是COL1,您希望将其设置为35个字符(根据链接,+4是遗留目的所需要的,可能是注释中A.H.提到的开销)。


我正面临着同样的问题,试图将VARCHAR从32截断为8,并得到ERROR:值对于类型字符变化(8)太长。我希望尽可能地接近SQL,因为我使用的是自制的类似于jpa的结构,我们可能不得不根据客户的选择切换到不同的DBMS (PostgreSQL是默认的)。因此,我不想使用修改System表的技巧。

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

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

正如@raylu指出的那样,ALTER在表上获得了一个排他锁,因此所有其他操作将被延迟,直到它完成。


我发现了一个非常简单的方法来改变大小,即注释@Size(min = 1, max = 50),这是import javax.validation的一部分。约束”。 “进口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),

.....

)

好吧,我可能要迟到了,但是…

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

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中是以相同的方式存储的。


添加新列,用旧列替换新列,在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;

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

如果你把alter放到一个事务中,表不应该被锁定:

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

这对我来说非常有效,几秒钟就搞定了400,000多行。


试着运行下面的alter表:

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