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

rjm*_*nro 26 postgresql

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

我知道你不应该依赖列顺序 - 它对我正在做的事情并不重要 - 我只需要它以一种更令人愉悦的方式制作一些自动生成的东西,以便字段顺序匹配所有从pgadmin通过后端到前端的方式.

小智 22

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

例如.

# 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可能会有所不同.

  • 除了不受支持之外,这种技术在不考虑其内容的情况下交换列名称(尽管答案被接受)并不是问题所在.如果要求它,则应使用"ALTER TABLE ... RENAME COLUMN ......",因为它可以实现相同的结果但可靠. (5认同)
  • 这非常棘手,有几个系统对象引用了列号.当您翻转第2列和第3列时,当对象需要第2列中的内容时,您将遇到麻烦.现在这是nr.3,您的数据库现已损坏.看看pg_constraint,这可以是一个数据库保护程序. (4认同)
  • @x-yuri:不,这个建议非常有害,应该删除。它可能看起来像是在起作用并造成损害,直到为时已晚。 (4认同)
  • 这样您就会丢失表中的所有数据. (3认同)

Tom*_*zky 16

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

  1. 禁用对数据库的写访问权限
    这是必不可少的,否则在开始下一个点之后的任何更改都将丢失
  2. pg_dump --create --column-inserts databasename > databasename.pgdump.sql
  3. 编辑CREATE TABLEdatabasename.pgdump.sql中的适当语句
    如果文件对于编辑器来说太大,只需使用split命令将其拆分,编辑,然后使用汇编回来cat
  4. drop database databasename
    你有最近的备份,为了以防万一,你呢?
  5. psql --single-transaction -f databasename.pgdump.sql
    如果你不使用--single-transaction它会很慢

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


Erw*_*ter 8

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

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

  • "允许对列位置进行重新排序的想法不是postgresql开发人员所反对的,更是一个没有人上前去做这项工作的情况." 引自http://wiki.postgresql.org/wiki/Alter_column_position如果有人想要添加该功能......本文最后概述了启动步骤. (2认同)
  • +1,虽然我认为每个人都知道它.从伊戈尔内曼的回应来看,有些人似乎没有理由.我认为通常情况下此功能很有用,尤其是 对于像我这样的OCD-in-schema人.正如Tom Lane指出的那样,我们还需要更改"视图,外键,索引,默认值,规则等".和所有其他依赖于列的东西.这实际上似乎并不是那么难以想象,即使只是使用PL/pgSQL.我想我们大多数人在这个问题上遇到的困难都是"只要我有时间". (2认同)

Tur*_*rgs 8

我也想要同样的。是的,顺序对于我的用例来说并不重要,但它只会让我烦恼:)

我正在采取的解决方法如下。

此方法将确保您保留任何现有数据,

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

当前表顺序:

id, name, email
Run Code Online (Sandbox Code Playgroud)

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

在这个例子中,我想email在 之前name

CREATE TABLE mytable_tmp
(
  id SERIAL PRIMARY KEY,
  email text,
  name text
);
Run Code Online (Sandbox Code Playgroud)

2. 将现有表中的所有数据插入到新表中。

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

3. 删除旧表。

DROP TABLE mytable;
Run Code Online (Sandbox Code Playgroud)

4. 将新表从“临时名称”重命名为“正确名称”。

ALTER TABLE mytable_tmp RENAME TO mytable;
Run Code Online (Sandbox Code Playgroud)

5. 重新添加您以前拥有的任何索引。

CREATE INDEX ...
Run Code Online (Sandbox Code Playgroud)

6. 重置主键增量的 ID 序列。

SELECT setval('public.mytable_id_seq', max(id)) FROM mytable;
Run Code Online (Sandbox Code Playgroud)


小智 5

您不能使用 postgres 就地更改列的顺序。但是,您可以通过视图解决您的问题。出于报告查询的目的,它看起来就像一个表格。就像是:

create view my_view as select * from my_table order by some_col;
Run Code Online (Sandbox Code Playgroud)

如果您的数据必须保持原位,您将不得不兼顾

您可以使用视图创建一个包含重新排序的列的新表,如下所示:

-- foo is the table you want to re-order columns in
drop table foo;
CREATE TABLE foo ( moo integer, bar character varying(10), baz date ); 
insert into foo (moo, bar, baz) values (34, 'yadz', now()); 
insert into foo (moo, bar, baz) values (12, 'blerp', now()); 
select * from foo; 
    ???????????????????????????? 
    ? moo ?  bar  ?    baz     ? 
    ???????????????????????????? 
    ?  34 ? yadz  ? 2021-04-07 ? 
    ?  12 ? blerp ? 2021-04-07 ? 
    ???????????????????????????? 
-- Define your reordered columns here, don't forget one, 
-- or it'll be missing from the replacement.
drop view if exists my_view; 
create view my_view as ( select moo, baz, bar from foo );  
select * from my_view; 
DROP TABLE IF EXISTS foo2; 
--foo2 is your replacement table that has columns ordered correctly
create table foo2 as select * from my_view; 
select * from foo2;
--finally drop the view and the original table and rename
DROP VIEW my_view; 
DROP TABLE foo; 
ALTER TABLE foo2 RENAME TO foo; 
select * from foo;
    ???????????????????????????? 
    ? moo ?    baz     ?  bar  ? 
    ???????????????????????????? 
    ?  34 ? 2021-04-07 ? yadz  ? 
    ?  12 ? 2021-04-07 ? blerp ? 
    ???????????????????????????? 
Run Code Online (Sandbox Code Playgroud)

  • 这不会改变列的顺序,而是基于列改变数据的顺序。 (2认同)