【问题标题】:Postgresql - change the size of a varchar column to lower lengthPostgresql - 将 varchar 列的大小更改为较低的长度
【发布时间】: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/…

标签: sql postgresql varchar alter-table


【解决方案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;
      

      【讨论】:

        猜你喜欢
        • 2012-02-08
        • 1970-01-01
        • 1970-01-01
        • 2018-10-17
        • 2023-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        相关资源
        最近更新 更多