varchar(x) 与“text CHECK ( char_length(x) )”一样快吗?

Eva*_*oll 5 postgresql varchar check-constraints

在 Twitter 交流中西蒙·韦斯特 (Simon West)向布兰杜尔 (Brandur)询问,

\n\n
\n

出于兴趣,为什么使用email TEXT CHECK (char_length(email) <= 255)而不是email VARCHAR(255)?我以前没有见过这种模式

\n
\n\n

布兰杜尔回应说,

\n\n
\n

很好的问题!

\n\n

(1) VARCHAR 和 TEXT 在 Postgres 中的性能相同(请参阅https://www.postgresql.org/docs/current/static/datatype-character.html \xe2\x80\xa6中的“提示”框)。\n 。

\n\n

(2) 如果您想更改长度,ALTER TABLE则需要独占锁(请参阅https://www.postgresql.org/docs/current/static/sql-altertable.html \xe2\x80\xa6)。改变CHECK是即时的。\n 当回答一个引起质疑的问题时text CHECK (char_length(email) <= 255)vsvarchar(255)

\n
\n\n

这两个断言中的第一个断言(粗体)是否严格正确?

\n\n

如果对第二个主张感兴趣,请查看此问题

\n

Eva*_*oll 4

不,这并不完全正确。这是链接文档的内容

除了使用空白填充类型时增加的存储空间以及在存储到长度受限列时需要一些额外的 CPU 周期来检查长度之外,这三种类型之间没有性能差异。虽然character(n)在其他一些数据库系统中具有性能优势,但在PostgreSQL中没有这样的优势;事实上,由于额外的存储成本,character(n) 通常是三者中最慢的。在大多数情况下,应该使用文本或字符变化。

所以本质上

  • 当列不受约束时,没有理由使用varcharover 。text在某些数据库中,情况并非如此,即实现诸如VARCHAR(max).
  • 当它被限制在列中时,它会变慢。使用varchar(x)超过是有原因的text CHECK ( length(x) <= x)

例子

你可以很容易地看到这一点,

\timing 1

CREATE TABLE foo (
  x varchar(255) NOT NULL
);

CREATE TABLE bar (
  x text NOT NULL 
    CHECK (char_length(x) <= 255)
);

INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);

INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);

INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);

INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);
Run Code Online (Sandbox Code Playgroud)

注意,char_lengthlength这里是相同的。他们内部调用textlen.

结果

test=# INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
Time: 1156.529 ms
test=# INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);
Time: 1268.869 ms
test=# INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
Time: 1107.869 ms
test=# INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);
Time: 1283.043 ms
test=# INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
Time: 1121.788 ms
test=# INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);
Time: 1238.194 ms
test=# INSERT INTO foo SELECT 'x' FROM generate_series(1,1e6);
Time: 1116.421 ms
test=# INSERT INTO bar SELECT 'x' FROM generate_series(1,1e6);
Time: 1248.272 ms
Run Code Online (Sandbox Code Playgroud)

在每种情况下,插入速度bar都较慢

重新检查类型约束

不过,我也希望检查会转移到使用相同类型的表上。这种优化也许有一天是可能的,但遗憾的是没有喜悦,

test=# CREATE TABLE foo2 ( LIKE foo INCLUDING ALL );
Time: 7.143 ms
test=# CREATE TABLE bar2 ( LIKE bar INCLUDING ALL );
CREATE TABLE
Time: 10.762 ms
test=# INSERT INTO foo2 TABLE foo;
Time: 3613.517 ms
test=# INSERT INTO bar2 TABLE bar;
Time: 4061.650 ms
Run Code Online (Sandbox Code Playgroud)

尽管 varchar 形式仍然明显更快。

其他注意事项

由于它与推文主题的电子邮件相关,因此这篇文章也可能有用

  • 是的,您观察到的差异可能是因为“CHECK”检查比“VARCHAR(n)”长度检查稍微昂贵。尽管如此,当我想要将“VARCHAR(50)”转换为“VARCHAR(250)”时,我会在可能的表锁上的每个 INSERT 上进行亚毫秒差异。 (2认同)