Postgresql - 将 varchar 列的大小更改为较低的长度
Posted
技术标签:
【中文标题】Postgresql - 将 varchar 列的大小更改为较低的长度【英文标题】:Postgresql - change the size of a varchar column to lower length 【发布时间】:2011-12-05 11:16:12 【问题描述】:我对一个非常大的表(近 3000 万行)上的 ALTER TABLE
命令有疑问。
其中一列是varchar(255)
,我想将其调整为varchar(40)
。
基本上,我想通过运行以下命令来更改我的列:
ALTER TABLE mytable ALTER COLUMN mycolumn TYPE varchar(40);
如果进程很长,我没有问题,但在ALTER TABLE
命令期间,我的表似乎不再可读。
有没有更聪明的方法?也许添加一个新列,从旧列复制值,删除旧列,最后重命名新列?
注意:我使用的是 PostgreSQL 9.0。
【问题讨论】:
明确一点:你知道,resizing
不会让表格占用更少的空间吗?
即使在我的情况下?我的意思是该列的最大大小为 40 个字符(所以八位字节)而不是 255 个?
如果你对 PostgreSQL 说varchar(255)
,那么它将不为实际长度为 40 字节的值分配 255 字节。它将分配 40 个字节(加上一些内部开销)。唯一会 be changed by the
ALTER TABLE` 的是您可以在该列中存储的最大字节数,而不会从 PG 收到错误。
关于开销 A.H. 提到:What is the overhead for varchar(n)?
在此处查看答案以获取更新dba.stackexchange.com/questions/189890/…
【参考方案1】:
在 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) |
【讨论】:
请注意,它仅在您指定 更大 尺寸 (30 > 10) 时才有效。如果尺寸更小,你会得到the same error than I had。 如果您通过 ALTER TABLE 查询降低 varchar 大小,除非多个行包含超过新尺寸。 @Tell,很有趣。这是否意味着 Postgres 会对表进行全面扫描,或者以某种方式在其统计信息中保留最大大小?【参考方案2】:Resize a column in a PostgreSQL table without changing data 有关于如何执行此操作的说明。您必须破解数据库目录数据。正式执行此操作的唯一方法是使用 ALTER TABLE,正如您所注意到的,更改将在整个表运行时锁定并重写整个表。
在更改此内容之前,请务必阅读文档的 Character Types 部分。这里需要注意各种奇怪的情况。将值存储到行中时完成长度检查。如果你在那里破解一个下限,那根本不会减少现有值的大小。在进行更改后,您最好扫描整个表以查找字段长度大于 40 个字符的行。你需要弄清楚如何手动截断这些——所以你只是在超大的那些上恢复了一些锁——因为如果有人试图更新该行上的任何东西,它现在会因为太大而拒绝它,此时它用于存储该行的新版本。用户的欢闹随之而来。
VARCHAR 是一种糟糕的类型,它存在于 PostgreSQL 中只是为了符合其相关的 SQL 标准的糟糕部分。如果您不关心多数据库兼容性,请考虑将数据存储为 TEXT 并添加约束以限制其长度。您可以在没有此表锁定/重写问题的情况下更改约束,并且它们可以进行更多的完整性检查,而不仅仅是弱长度检查。
【讨论】:
感谢您的回答。我会检查你的链接。我不担心手动大小检查,因为我所有内容的最大大小为 40 个字符。我需要阅读有关 TEXT 约束的更多信息,因为我认为 VARCHAR 更好地检查 lentgh :) 更改 varchar 长度不会重写表。它只是像检查约束一样检查整个表的约束长度。如果您增加长度,则无需执行任何操作,只需下一次插入或更新将接受更大的长度。如果减少长度并且所有行都通过了新的更小的约束,Pg 除了允许下一次插入或更新仅写入新长度之外,不会采取任何进一步的行动。 @bigown,澄清一下,您的声明是 only true for PostgreSQL 9.2+,而不是旧的。【参考方案3】:好吧,我可能迟到了,但是......
您无需调整列的大小!
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。
【讨论】:
添加更多关于“为什么”的信息永远不会太晚!感谢您提供所有这些信息 有时您需要在数据库结构上保持一致。即使 2 列没有关系,它们也可以在概念上存在关系,例如检查模型 EAV。 是的,但是您已经过度计算事务了。想一想,您声明了一个带有暗淡的列。 1024,你存储 10K,具有可变数量的暗淡的行。您让 RDBMS(不是他的案例 postgres)为您的列计算“最佳”大小。 @FrancoGil:首先,我无法想象“计算最佳尺寸”的开销绝对可以忽略不计。其次,无论您如何定义列,Postgres 都会这样做,如果您存储相同的数据,则 VARCHAR(40) 和 VARCHAR(1024) 的磁盘格式将相同。【参考方案4】:我在尝试将 VARCHAR 从 32 截断为 8 并获得 ERROR: value too long for type character varying(8)
时遇到了同样的问题。我希望尽可能接近 SQL,因为我使用的是自制的类似 JPA 的结构,我们可能必须根据客户的选择切换到不同的 DBMS(PostgreSQL 是默认的)。因此,我不想使用更改系统表的技巧。
我在ALTER TABLE
中使用了USING
语句:
ALTER TABLE "MY_TABLE" ALTER COLUMN "MyColumn" TYPE varchar(8)
USING substr("MyColumn", 1, 8)
正如@raylu 所指出的,ALTER
在表上获得了一个排他锁,因此所有其他操作都将被延迟,直到它完成。
【讨论】:
ALTER
获取表上的排他锁并阻止所有其他操作【参考方案5】:
添加新列并用旧列替换新列对我有用,在 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】:如果您将更改放入事务中,则不应锁定表:
BEGIN;
ALTER TABLE "public"."mytable" ALTER COLUMN "mycolumn" TYPE varchar(40);
COMMIT;
这对我来说非常有用,在超过 400k 行的表上只需几秒钟。
【讨论】:
您为什么希望显式事务包装器更改ALTER
语句的锁定行为?它没有。
自己尝试一下,无论有没有事务包装器,您都会发现巨大的差异。
您的回答在原则上不正确。任何没有显式事务包装器的 DDL 语句都隐式地在事务内运行。显式事务唯一可能的影响是锁被保持更长 - 直到显式COMMIT
。仅当您想将更多命令放入同一个事务中时,包装器才有意义。
你是完全正确的,但我坚持:试试自己,继续。然后问为什么不以同样的方式工作。
对 Postgres 9.3 没有帮助。【参考方案7】:
这是 Greg Smith 描述的页面的 the cache。如果它也死了,alter 语句如下所示:
UPDATE pg_attribute SET atttypmod = 35+4
WHERE attrelid = 'TABLE1'::regclass
AND attname = 'COL1';
如果您的表是 TABLE1,列是 COL1,并且您希望将其设置为 35 个字符(根据链接,遗留用途需要 +4,可能是 cmets 中 AH 所指的开销)。
【讨论】:
【参考方案8】:我找到了一种非常简单的方法来更改大小,即注释 @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),
.....
)
【讨论】:
感谢您的帖子!请不要在您的帖子中使用签名/标语。您的用户框算作您的签名,您可以使用您的个人资料发布您喜欢的任何关于您自己的信息。 FAQ on signatures/taglines【参考方案9】:尝试按照 alter table 运行:
ALTER TABLE public.users
ALTER COLUMN "password" TYPE varchar(300)
USING "password"::varchar;
【讨论】:
以上是关于Postgresql - 将 varchar 列的大小更改为较低的长度的主要内容,如果未能解决你的问题,请参考以下文章
在 PostgreSQL 中将数据类型“Varchar”更改为“TimeStamp”
将不同格式列的String数据类型的日期转换为PostgreSQL中的日期数据类型
PostGreSql 使用拥有 Max 子句获取两列的唯一组合