【问题标题】:String field length in Postgres SQLPostgresql中的字符串字段长度
【发布时间】:2014-07-22 05:01:18
【问题描述】:

我在 SQL 数据库中有一个字符串,代表一个 url。有些网址很短,有些很长。我真的不知道我可能遇到的最长 URL 是多少,所以为了安全起见,我会取一个较大的值,例如 256 或 512。

当我定义最大字符串长度时(例如使用 SQLAlchemy):

url_field = Column(String(256))

即使实际字符串更短,这是否会占用每一行的空间(存储空间)?

我假设这与实现细节有关。我正在使用postgreSQL,但对sqlite,mysql也很感兴趣。

【问题讨论】:

标签: sql postgresql sqlalchemy


【解决方案1】:

通常数据库存储引擎可以做很多你意想不到的事情。但基本上,有两种文本字段可以提示内部发生的情况。

char 和 varchar。 Char 会给你一个固定的字段列,根据 sql 会话中的选项,你可能会收到空格填充的字符串。 Varchar 用于达到某个最大长度的文本字段。

Varchar 字段可以存储为块外部的指针,以便块在查询中保持可预测的大小 - 但这是一个实现细节,可能因数据库而异。

【讨论】:

  • 为什么我要指定长度,而不是只使用 varchar 呢?使用 char(n) 而不是 varchar(n) 的性能优势是什么
  • Varchar 仍然需要数据库正确管理空间以均匀大小的内存块或页面。数据库可以决定将数据集放在块的末尾并使用“块内”指针。因此,列在块中占用了可预测的空间,并且可变数据放在后面,在大型 varchars 上,db 可能需要读取第二个页面来读取 varchar 的数据。 char 的性能优势通常是,您不需要从页面中获取 varchar 数据,它就在那里。但正如我所说:这可能会因实现而异,并受数据库优化器的影响。
  • char(n) 是固定宽度格式时代的可怕遗物(想想FORTRAN 77),应该避免 IMO。在 PostgreSQL 中,它与 varchar 相比没有优势,但也有很多劣势。 varcharchar 都是 TOASTable 并且受外线存储的影响,因此您的区别不正确。无论如何,如果您想内联存储任何字段,您可以使用ALTER TABLE ... ALTER COLUMN ... SET STORAGE 来完成,无论数据类型如何。请参阅有关字符类型和 TOAST 的文档。
【解决方案2】:

postgreSQL、sqllite 和 mysql 都应用 sql 标准来存储 varchar 和 chars。基本上是这样的:

SQL 定义了两种主要的字符类型:character varying(n) 和 字符(n),其中 n 是一个正整数。这两种类型都可以 存储最多 n 个字符的字符串。尝试存储 将较长的字符串放入这些类型的列中会导致错误, 除非多余的字符都是空格,在这种情况下字符串 将被截断到最大长度。 (这有点奇怪 SQL标准要求例外。)如果要存储的字符串 比声明的长度短,字符类型的值将是 空格填充;类型字符变化的值将简单地存储 较短的字符串。

如果一个人明确地将一个值转换为字符变量(n)或 字符(n),那么一个超长的值将被截断为n 字符而不引发错误。 (这也是 SQL 所要求的 标准。)

符号 varchar(n) 和 char(n) 是字符的别名 变化(n)和字符(n),分别。没有长度的字符 说明符等同于字符(1)。如果使用字符变化 没有长度说明符,该类型接受任何大小的字符串。这 后者是 PostgreSQL 扩展。

参考:

【讨论】:

    【解决方案3】:

    在 PostgreSQL 中,character(n) 基本上只是 varchar,在输入/输出上有空格填充。这很笨拙,应该避免。它消耗与填充到最大长度的varchartext 字段相同的存储空间(见下文)。 char(n) 是一个历史缺陷,应该避免 - 至少在 PostgreSQL 中它没有任何优势,并且与 left(...) 之类的东西有一些奇怪的怪癖。

    varchar(n)varchartext 都使用相同的存储空间 - 您提供的不带填充的字符串的长度。它仅使用字符实际所需的存储空间,而与长度限制无关。此外,如果字符串为空,PostgreSQL 根本不会为它存储值(甚至没有长度标头),它只是在记录的空位图中设置空位。

    合格的varchar(n) 与不合格的varchar 基本相同,但check 约束length(colname) < n

    尽管其他一些 cmets/answers 在说什么,char(n)varcharvarchar(n)text 都是 TOASTable 类型。它们都可以离线存储和/或压缩。控制存储使用ALTER TABLE ... ALTER COLUMN ... SET STORAGE

    如果您不知道需要的最大长度,只需使用text 或不合格的varchar。没有空间损失。

    有关更多详细信息,请参阅the documentation on character data types,有关它们如何存储的一些内部信息,请参阅database physical storage,尤其是TOAST

    演示:

    CREATE TABLE somechars(c10 char(10), vc10 varchar(10), vc varchar, t text);
    insert into somechars(c10) values ('  abcdef ');
    insert into somechars(vc10) values ('  abcdef ');
    insert into somechars(vc) values ('  abcdef ');
    insert into somechars(t) values ('  abcdef ');
    

    每个列的查询输出:

    SELECT 'c10', pg_column_size(c10), octet_length(c10), length(c10) 
    from somechars where c10 is not null;
    

    是:

     ?column? | pg_column_size | octet_length | length 
     c10      |             11 |           10 |      8
     vc10     |             10 |            9 |      9
     vc       |             10 |            9 |      9
     t        |             10 |            9 |      9
    

    pg_column_size 是字段中数据的磁盘大小。 octet_length 是没有标题的未压缩大小。 length 是“逻辑”字符串长度。

    如您所见,char 字段已填充。它浪费了空间,而且它也给length 提供了一个非常令人惊讶的结果,因为输入是 9 个字符,而不是 8 个字符。那是因为 Pg 无法区分你自己输入的前导空格和前导空格之间的区别添加为填充。

    所以,不要使用char(n)

    顺便说一句,如果我正在设计一个数据库,我从不使用varchar(n)char(n)。我只是使用 text 类型并添加适当的 check 约束,如果有值的应用程序要求。我认为varchar(n) 在标准中有点小瑕疵,但我认为它对于具有磁盘布局且大小限制可能会影响存储的数据库很有用。

    【讨论】:

    • 关于字符串的“逻辑长度”主题,你知道 PostgreSQL 是否只尊重基本多语言平面 (0-FFFF) 中的 unicode 代码点,还是使用晦涩难懂的文字、符号,尤其是表情符号。此外,如何修改外观的表情符号,如脸上的肤色、国旗?我正在开发客户端软件并试图限制输入长度以匹配我们后端的最大值,但还没有找到记录的位置。
    • @smallduck PostgreSQL 不对 unicode 进行规范化,并且会将代理对、分解的字形等视为多个字符。 postgresql.org/message-id/53E179E1.3060404@2ndquadrant.com 。运行select length('á') AS decomposed, length('á') AS precomposed;(完全复制粘贴)查看。如果您的浏览器、终端等未对 unicode 进行规范化,您将分别获得长度 2 和 1。
    • v9.2 开始,我更喜欢varchar(n) 而不是text,因为它允许我增加长度而无需重新检查。无论您是否增加字符长度,更改检查约束都会强制重新检查。该检查使用AccessExclusiveLock,阻止读取,使其不适合生产数据库。
    猜你喜欢
    • 2015-06-17
    • 1970-01-01
    • 2019-02-05
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    相关资源
    最近更新 更多