33

Is it possible to change the natural order of columns in Postgres 8.1?

I know that you shouldn't rely on column order - it's not essential to what I am doing - I only need it to make some auto-generated stuff come out in a way that is more pleasing, so that the field order matches all the way from pgadmin through the back end and out to the front end.

4

8 回答 8

22

You can actually just straight up change the column order, but I'd hardly recommend it, and you should be very careful if you decide to do it.

eg.

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

Now for the tricky bit, you need to connect to your database using the postgres user so you can modify the system tables.

# 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 is a unique column, so you need to use a temporary value when you're modifying the column numbers as such:

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

Again, because this is playing around with database system tables, use extreme caution if you feel you really need to do this.

This is working as of postgres 8.3, with prior versions, your milage may vary.

于 2009-03-18T03:06:10.273 回答
16

If your database is not very big and you can afford some downtime then you can:

  1. Disable write access to the database
    this is essential as otherwise any changes after starting the next point will be lost
  2. pg_dump --create --column-inserts databasename > databasename.pgdump.sql
  3. Edit apropriate CREATE TABLE statement in databasename.pgdump.sql
    If the file is too big for your editor just split it using split command, edit, then assemble back using cat
  4. drop database databasename
    You do have a recent backup, just in case, do you?
  5. psql --single-transaction -f databasename.pgdump.sql
    If you don't use --single-transaction it will be very slow

If you use so called large objects make sure they are included in the dump. I'm not sure if they are by default in 8.1.

于 2008-09-24T14:25:10.397 回答
10

I have asked that question in pgsql-admin in 2007. Tom Lane himself declared it practically unfeasible to change the order in the catalogs.

Clarification: this applies for users with the present tools. Does not mean, it could not be implemented. IMO, it should be.
Still true for Postgres 12.

于 2011-09-16T01:02:03.467 回答
4

You cannot change the order of columns in-place with postgres. However you can solve your problem with a view. For the purposes of your reporting query, it will look just like a table. Something like:

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

If your data must remain in-place you'll have to juggle

You can use a view to create a new table with the reordered columns like this:

-- 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 │ 
    └─────┴────────────┴───────┘ 
于 2008-09-24T12:14:45.153 回答
4

I'm wanting the same. Yes, order isn't essential for my use-case, but it just rubs me the wrong way :)

What I'm doing to resolve it is as follows.

This method will ensure you KEEP any existing data,

  1. Create a new version of the table using the ordering I want, using a temporary name.
  2. Insert all data into that new table from the existing one.
  3. Drop the old table.
  4. Rename the new table to the "proper name" from "temporary name".
  5. Re-add any indexes you previously had.
  6. Reset ID sequence for primary key increments.

Current table order:

id, name, email

1. Create a new version of the table using the ordering I want, using a temporary name.

In this example, I want email to be before name.

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

2. Insert all data into that new table from the existing one.

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

3. Drop the old table.

DROP TABLE mytable;

4. Rename the new table to the "proper name" from "temporary name".

ALTER TABLE mytable_tmp RENAME TO mytable;

5. Re-add any indexes you previously had.

CREATE INDEX ...

6. Reset ID sequence for primary key increments.

SELECT setval('public.mytable_id_seq', max(id)) FROM mytable;
于 2018-02-01T23:48:24.507 回答
1

You can get the column ordering that you want by creating a new table and selecting columns of the old table in the order that you want them to present:

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

Note that this copies data only, not modifiers, constraints, indexes, etc..

Once the new table is modified the way you want, drop the original and alter the name of the new one:

BEGIN;
DROP TABLE test;
ALTER TABLE test_new RENAME TO test;
COMMIT;
于 2016-11-30T21:28:45.157 回答
0

Unfortunately, no, it's not. Column order is entirely up to Postgres.

于 2008-09-24T10:40:12.870 回答
0

Specifying the column order in the query is the only reliable (and sane) way. That said, you can usually get a different ordering by altering the table as shown in the example below as the columns are usually (not guaranteed to be) returned in the order they were added to the table.

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=#
于 2008-09-24T10:42:47.637 回答