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

时间:2008-09-24 10:39:12

标签: postgresql

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

我知道你不应该依赖列顺序 - 它不是必要的我正在做的事情 - 我只需要它来制作一些自动生成的东西以更多的方式出现令人愉悦,因此字段顺序从pgadmin到后端一直匹配到前端。

8 个答案:

答案 0 :(得分:20)

你实际上可以直接改变列顺序,但我几乎不推荐它,如果你决定这样做,你应该非常小心。

例如

# 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一样,对于以前的版本,你的milage可能会有所不同。

答案 1 :(得分:16)

如果您的数据库不是很大并且您可以承受一些停机时间,那么您可以:

  1. 禁用对数据库的写访问权限 这是必不可少的,否则在开始下一个点之后的任何更改都将丢失
  2. pg_dump --create --column-inserts databasename > databasename.pgdump.sql
  3. 在databasename.pgdump.sql中编辑适当的CREATE TABLE语句 如果文件对于您的编辑器来说太大,只需使用split命令将其拆分,编辑,然后使用cat
  4. 进行汇编
  5. drop database databasename
    你有最近的备份,为了以防万一,是吗?
  6. psql --single-transaction -f databasename.pgdump.sql
    如果您不使用--single-transaction,则会非常慢
  7. 如果使用所谓的大对象,请确保它们包含在转储中。我不确定它们是否默认为8.1。

答案 2 :(得分:7)

我在2007年的pgsql-admin中问了这个问题.Tom Lane本人宣称改变目录中的顺序实际上是不可行的。

澄清:对于用户,使用现有工具。不代表,它无法实施。 IMO应该是。
对于Postgres 11来说仍然如此。

答案 3 :(得分:4)

正如提到的其他答案,你不能改变列的顺序,这取决于postgres。您可以(并且应该!)通过视图解决您的问题。出于报告查询的目的,它看起来就像一个表。类似的东西:

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

答案 4 :(得分:1)

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

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=#

答案 5 :(得分:0)

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

答案 6 :(得分:0)

您可以通过创建新表并按照希望它们显示的顺序选择旧表的列来获取所需的列顺序:

CREATE TABLE test_new AS SELECT b, c, a FROM test;
SELECT * from test_new;
 b | c | a 
---+---+---
 2 | 3 | 1
(1 row)

请注意,这仅复制数据,而不是修饰符,约束,索引等。

按照您想要的方式修改新表后,删除原始表并更改新表的名称:

BEGIN;
DROP TABLE test;
ALTER TABLE test_new RENAME TO test;
COMMIT;

答案 7 :(得分:0)

我也想要一样。是的,现在订购它是必不可少的,但它只是以错误的方式揉我:)

我正在做的解决方法如下。

此方法将确保您保留所有现有数据

  1. 使用临时名称,使用我想要的顺序创建表的新版本。
  2. 将所有数据从现有数据插入到新表中。
  3. 放下旧桌子。
  4. 将新表重命名为“临时名称”中的“专有名称”。
  5. 重新添加您之前拥有的任何索引。
  6. 重置主键增量的ID序列。
  7. 当前的表格顺序:

    id, name, email
    

    <强> 1。使用我想要的顺序使用临时名称创建表的新版本。

    在此示例中,我希望emailname之前。

    CREATE TABLE mytable_tmp
    (
      id SERIAL PRIMARY KEY,
      email text,
      name text
    );
    

    <强> 2。将所有数据从现有数据插入到新表中。

    INSERT INTO mytable_tmp   --- << new tmp table
    (
      id
    , email
    , name
    )
    SELECT
      id
    , email
    , name
    FROM mytable;  --- << this is the existing table
    

    第3。放下旧桌子。

    DROP TABLE mytable;
    

    <强> 4。将新表重命名为“临时名称”中的“专有名称”。

    ALTER TABLE mytable_tmp RENAME TO mytable;
    

    <强> 5。重新添加您之前拥有的任何索引。

    CREATE INDEX ...
    

    <强> 6。重置主键增量的ID序列。

    SELECT setval('public.mytable_id_seq', max(id)) FROM mytable;