为什么 PostgreSQL 不允许对列重新排序?

use*_*437 9 postgresql

无数次,例如现在,我需要更改表中列的顺序。

例如,当我改进CREATE TABLE库中的 SQL 代码时,通过在表中的某处添加一列,然后我需要使我的“实时”表反映这些更改。当我添加新列时,它被放置在最后/“右侧”,并且拒绝让我指定它应该去的位置。

虽然这可能对性能影响很小或没有影响,但知道“实时”表和 SQL 代码中定义的表之间的列顺序不同是非常烦人的。

我读过 Stack Exchange 上有关此问题的问题,并多年来多次询问 PG 开发人员。他们莫名其妙地不支持对列重新排序,而是提出可笑的“解决方案”,例如转储整个数据库并将其重新加载,或者创建一个单独的“视图”,或者许多其他奇怪的解决方法,这些解决方法是不切实际的,并且在许多情况下比顺序错误更糟糕的情况。

我不是在问如何做到这一点,因为这显然是不可能的。我只是想知道为什么这是不可能的。我无法想象对列重新排序存在真正的技术挑战,但也许确实存在?从我从 PG 开发者/社区那里得到的态度/氛围来看,这对我来说就像是他们可以轻松修复但出于某些哲学原因而无法修复的事情之一。

真正的原因是什么?对于 PG 来说,简单地交换一些内部数字以使列获得所需的顺序真的那么困难吗?让两个相关的列永远彼此远离看起来真的很难看,这是对我在最初创建表时没有立即考虑后面的列的惩罚。

Lau*_*lbe 6

这有一个非常重要的概念原因,也有为什么没人愿意实施它的原因。

为什么列顺序无关?

在 SQL 中,表中列的顺序仅在您使用时才重要

SELECT * FROM ...
Run Code Online (Sandbox Code Playgroud)

这是你永远不应该在代码中使用的东西:

  • 如果有人添加或删除列,结果集的列将突然更改,并且您不会收到数据库错误,但客户端应用程序可能会对现在返回的不同数据感到惊讶。

  • 如果连接表,您可能会突然得到一个结果集,其中多个列具有相同的名称。这可能会在客户端造成混乱,并且会使CREATE VIEW语句彻底失败。

  • 即使它不会导致上述问题之一,您通常也会最终获取并不真正需要的行,这会导致不必要的流量、处理和可能的 deTOAST。

如果您显式指定列表中的列SELECT,则表中列的顺序无关。

到底为什么列顺序会相关呢?

实际上,有两个小点可以使列顺序产生影响:

  • 对齐和填充:

    PostgreSQL 中的每种数据类型都有一个对齐方式bigint例如,8 个字节的对齐意味着数据只能存储在 8 的倍数的内存地址处。现在,如果在(对齐 2)之后紧接着有一个(对齐 8) smallint,则最终可能会得到这些列之间浪费了 6 个字节的“填充”空间。

  • 从行中提取数据:

    如果要从一行中提取第 100 列,则必须跳过前 99 列。这比访问第一列的成本更高。

但在实践中,像这样的性能和存储空间优化并没有太大的效果。

为什么没有人实施列重新排序?

由于表行按列顺序存储,因此更改列顺序将意味着重写整个表。因此,它不会比导出和导入快得多。

并且已经有一些方法可以解决这个“缺点”:

  • “步行”:

    BEGIN;
    CREATE TABLE tab_copy (col1 type1, col2 type2, ...);
    INSERT INTO tab_copy SELECT col12, col5, col1, ... FROM tab;
    DROP TABLE tab;
    ALTER TABLE tab_copy RENAME TO tab;
    COMMIT;
    
    Run Code Online (Sandbox Code Playgroud)

    这过于简单化了:您还必须处理外键和其他约束。

    但本质上您已经可以对列进行重新排序,但它并不像想象的那么简单。

  • 您可以使用视图:

    CREATE VIEW newtab AS SELECT col12, col5, col1, ... FROM tab;
    
    Run Code Online (Sandbox Code Playgroud)

    SELECT此类视图可以是、INSERT UPDATE和的目标DELETE

为了实现这一点,已经做出了认真的努力,但事实证明,乍一看这个问题更加困难,而且由于有解决方法,所以解决这个问题的压力似乎不够高。

  • 不,你必须从用户的角度来看,而不是开发者的角度。开发人员并不关心表只要运行良好即可,但用户有其他考虑因素,排序是非常重要的部分。考虑一个随机排序的表,因此 userId 位于第 25 列,日期位于第 12 列,前 5 列不包含非常重要的业务数据(但仍然必须存在)。现在考虑一个具有完全相同信息的表,但所有重要的列都列在前面,并具有用户理解的业务逻辑。 (4认同)
  • 让我摆脱退休生活 :) (1) `[using select *] 是你永远不应该在代码中使用的东西`——也许吧,但我通常不关心代码中列的排序,我关心 CLI 和 ad - 临时查询,快速查找。(2) “只使用视图”的论点会产生不必要的额外资源和混乱。(3) 尚未实施的原因有很多,我不同意你提供的那些。我要补充的是,RhomiumToad 和其他人曾考虑改变大流行前的逻辑顺序。从那以后我就没有再关注 Postgres 社区了。 (2认同)
  • “没有人愿意花时间和精力在一些不太相关且已经可以通过其他方式完成的事情上”我完全不同意这一点。(1) 相关性是主观的,但我相信我们已经暗示过这一点。(2)它不能轻易地以其他方式完成,你的例子是努力并会导致错误。有多少表具有索引/约束/fk 和其他依赖项?对于简单的事情,您的示例可以工作,但是当您想要更改列顺序时,通常是在表成熟之后并且具有这些额外的复杂性 - 规避风险的开发人员不愿意废弃/重新创建 (2认同)