是否可以在Postgres中更改列的自然顺序?

时间:2020-03-06 14:39:14  来源:igfitidea点击:

是否可以在Postgres 8.1中更改列的自然顺序?

我知道我们不应该依赖列顺序,这对我在做什么并不重要,我只需要它可以使一些自动生成的内容以更令人愉悦的方式出现,从而使字段顺序从pgadmin从后端到前端。

解决方案

不幸的是,不是,不是。列顺序完全取决于Postgres。

在查询中指定列顺序是唯一可靠(合理)的方法。就是说,我们通常可以通过更改表来获得不同的顺序,如下例所示,因为通常(不保证返回)列的添加顺序是将它们添加到表的顺序。

postgres=# create table a(a int, b int, c int);
CREATE TABLE
postgres=# insert into a values (1,2,3);
INSERT 0 1
postgres=# select * from a;
 a | b | c
---+---+---
 1 | 2 | 3
(1 row)

postgres=# alter table a add column a2 int;
ALTER TABLE
postgres=# select * from a;
 a | b | c | a2
---+---+---+----
 1 | 2 | 3 |
(1 row)

postgres=# update a set a2 = a;
UPDATE 1
postgres=# alter table a drop column a;
ALTER TABLE
postgres=# alter table a rename column a2 to a;
ALTER TABLE
postgres=# select * from a;
 b | c | a
---+---+---
 2 | 3 | 1
(1 row)

postgres=#

正如其他答案所述,我们不能更改列的顺序,这取决于postgres。我们可以(并且应该!)使用视图解决问题。就报告查询而言,它看起来就像一个表。就像是:

create view my_view as
  select * from my_table
  order by some_col;

如果数据库不是很大,并且我们可以承受一些停机时间,则可以:

  • 禁用对数据库的写访问,这是必不可少的,否则开始下一个点之后的所有更改都将丢失
  • pg_dump --create --column-inserts数据库名> databasename.pgdump.sql
  • 在databasename.pgdump.sql中编辑适当的CREATE TABLE语句如果文件对于编辑器来说太大,只需使用split命令将其拆分,进行编辑,然后使用cat进行组合
  • drop database databasename我们确实有最近的备份,以防万一,是吗?
  • psql --single-transaction -f databasename.pgdump.sql如果不使用--single-transaction将会非常慢

如果使用所谓的大对象,请确保它们包含在转储中。我不确定它们是否默认为8.1.

实际上,我们可以直接改变列的顺序,但是我几乎不建议这样做,如果我们决定这样做,则应该非常小心。

例如。

# CREATE TABLE test (a int, b int, c int);
# INSERT INTO test VALUES (1,2,3);
# SELECT * FROM test;
 a | b | c 
---+---+---
 1 | 2 | 3
(1 row)

现在需要技巧的是,我们需要使用postgres用户连接到数据库,以便可以修改系统表。

# SELECT relname, relfilenode FROM pg_class WHERE relname='test';
 relname | relfilenode 
---------+-------------
 test_t  |       27666
(1 row)

# SELECT attrelid, attname, attnum FROM pg_attribute WHERE attrelid=27666;
 attrelid | attname  | attnum 
----------+----------+--------
    27666 | tableoid |     -7
    27666 | cmax     |     -6
    27666 | xmax     |     -5
    27666 | cmin     |     -4
    27666 | xmin     |     -3
    27666 | ctid     |     -1
    27666 | b        |      1
    27666 | a        |      2
    27666 | c        |      3
(9 rows)

attnum是唯一的列,因此在按如下方式修改列号时需要使用一个临时值:

# UPDATE pg_attribute SET attnum=4 WHERE attname='a' AND attrelid=27666;
UPDATE 1
# UPDATE pg_attribute SET attnum=1 WHERE attname='b' AND attrelid=27666;
UPDATE 1
# UPDATE pg_attribute SET attnum=2 WHERE attname='a' AND attrelid=27666;
UPDATE 1

# SELECT * FROM test;
 b | a | c 
---+---+---
 1 | 2 | 3
(1 row)

同样,由于这是数据库系统表的问题,如果我们确实需要执行此操作,请格外小心。

从postgres 8.3开始,此功能已开始运行,并且在以前的版本中,里程可能会有所不同。