【问题标题】:(var)char as the type of the column for performance?(var)char 作为性能列的类型?
【发布时间】:2014-05-21 10:48:52
【问题描述】:

我在 PostgreSQL 中有一个名为“状态”的列。首先它曾经是integer 类型的“status_id”。这些值保存在客户端上,因此服务器上有一个名为 statuses 的 no 表,我将在其中保留这些状态,然后对第一个表执行 inner join

我过去常常从客户端发送状态 ID(它们在客户端上有名称)。但是,在某些时候我明白我最好让服务器保持这些状态。不是在单独的表中,而是在第一个表中,我想让它们成为字符串。所以初始表将有一个字符串类型的status 列(更具体地说,varchar)。我读它不会那么慢。

总的来说,这是个好主意吗?我想这是因为每次都做inner join(以防我将状态保存在单独的表中)以及从客户端发送ID都很昂贵。

1) 我唯一担心的是status 列的类型应该是char,而不是varchar。我想它应该使它更有效。是这样吗?

2) 如果第一种情况是正确的,那么我不确定我能否使用完全相同数量的字符(例如 5 个字符)来命名所有状态。其中一些可能更长,一些更短。我该如何解决这个问题?

更新:

这不是取消国有化,因为我说的是 1 个单桌。没有也从来没有第二个名为 Statuses 的表,其中包含字段 (id, status_name)。

我想表达的是我可以使用 char(n) 作为 status_name 并在其上添加索引。那么它应该足够快。但是,用特定 (n) 个字符来命名所有状态可能是可能的,也可能是不可能的,这是唯一的问题。

【问题讨论】:

  • 您描述的过程是状态列的非规范化。这将导致它自己的问题。您确定不想保留“状态”表吗?在像这样的低基数表上,连接相对便宜。
  • @RobertHarvey 这不是非规范化。如果我有 2 个表并且仍然使用第一个表中的“状态”列,那将是非规范化。
  • 那为什么它被存储为status_id类型的integer
  • @RobertHarvey,因为它是一个整数。如果愿意,字符串表示形式会以枚举(Int、String)或 Map 的形式存储在客户端上。
  • 正确的做法是在数据库中创建一个状态表,以 status_id 作为主键。但是,不管你的船是什么。

标签: performance postgresql types normalization postgresql-9.2


【解决方案1】:

我不认为使用 char 或 varchar 代替整数是个好主意。很难预料它会比整数 PK 慢多少,但这种设计会更慢——当你加入更大的表时,影响会更可怕。如果可以,请改用 ENUM 类型。

http://www.postgresql.org/docs/9.2/static/datatype-enum.html

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

CREATE TABLE person (
    name text,
    current_mood mood
);
INSERT INTO person VALUES ('Moe', 'happy');
SELECT * FROM person WHERE current_mood = 'happy';
 name | current_mood 
------+--------------
 Moe  | happy
(1 row)

PostgreSQL varchar 和 char 类型非常相似。内部实现是相同的 - 由于空格相加,char 可以(这是悖论)稍微慢一点。

【讨论】:

  • I don't think so using char or varchar instead integer is good idea. - 如果我在上面添加索引,为什么不使用它?
  • 在服务器端使用枚举似乎是个好主意。
  • 索引不会用于小表 - varchar 比较比整数慢。
  • 我没有 2 张桌子。我只有一张桌子。
【解决方案2】:

我会更进一步。 永远不要使用过时的数据类型char(n),除非您知道必须这样做(出于兼容性或一些罕见的奇异原因)。该类型在现代数据库中完全没用。用空白字符填充字符串是无稽之谈,如果您必须这样做,您可以使用rpad() 以更便宜的方式进行数据检索。

SELECT rpad('short', 10) AS char_10_string;

varchartext 基本相同,并允许使用长度说明符:varchar(n)。我通常只使用text。如果我需要限制长度,我使用CHECK 约束。 Here's one example, why.

只要您可以改用简单的integer(或enum),它在各个方面都会更小更快。考虑将@Pavel's answer 替换为enum

至于:

因为每次做inner join (...) 都很昂贵

嗯,它的成本很低,但它通常比在主表中冗余保存状态的文本表示而不是便宜得多的整数便宜。这种谣言是由对database normalization 的概念有问题的人传播的。 enum 类型在这里是一种折衷方案 - 用于相对静态的值集。

【讨论】:

  • 我有时会使用char(n) 来表示固定宽度的短代码,例如ISO 代码。但更多的是作为一种记录手段,而不是任何技术目的。它使意图和约束更加清晰(我认为)
  • @a_horse_with_no_name:嗯,这是一个可能的用例。如果您知道字符串永远不会用空白填充,那么就没有什么坏处了。但是,违反字符串不会引发异常。我会考虑 textCHECK 约束 length(foo) = n
  • @a_horse_with_no_name,这就是我想在我的问题中传达的内容。我会使用 char(n) 但它可能并不总是可能的。但也许它可能是。
  • 我可以使用 char(n) 作为@a_horse_with_no_name 描述的“状态”。为什么不?我会在上面添加索引。至少我会摆脱内部连接。
  • @Alex:当然可以。这只是一个可怕的想法。主表和索引会变大变慢。字符数据必须考虑COLLATION 才能进行排序。更改状态需要更改具有该状态的所有行,而不是单行等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多