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 子句获取两列的唯一组合

在查询中查找导致 postgresql 异常的列。

如何在 Postgresql 中使用 ALTER 将 VARCHAR 类型更改为 DATETIME?

Django/PostgreSQL varchar 到 UUID