37

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.

rjmunro
  • 27,203
  • 20
  • 110
  • 132

8 Answers8

21

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.

Russell
  • 314
  • 2
  • 2
  • 6
    This is very tricky, several system object refer to a column number. When you flip columns 2 and 3, you will be in trouble when an object needs something from column 2. This is now nr. 3 and your database is now corrupt. Take a look at pg_constraint, this can be a database saver. – Frank Heikens Mar 27 '10 at 18:10
  • 6
    This way you lose all the data in your tables. – Peter Eisentraut Sep 16 '11 at 03:16
  • 7
    Besides being unsupported, this technique swaps column names without respect for their contents, which (despite the answer being accepted) is not what the question is asking. And if it was asking for that, `ALTER TABLE... RENAME COLUMN...` should be used instead, since it achieves the same result but reliably. – Daniel Vérité Nov 12 '14 at 14:57
  • 1
    Actually tried this. Table became useless and every query to the table disconnected the server. The good thing is that if you reverse back everything works fine again. – cachique May 11 '17 at 18:26
  • You might run into trouble at least [with indexes](https://dba.stackexchange.com/questions/307768/attribute-51-of-type-users-has-wrong-type). In some cases it probably might work out. In my case it was reversible. | @DanielVérité It does what's asked. It's just that generally it most likely won't work. – x-yuri Feb 21 '22 at 13:06
  • 1
    @x-yuri: No, this advise is actively harmful and should be removed. It may seem like it's working and cause damage until it's too late to reverse it. – Erwin Brandstetter Feb 21 '22 at 15:27
  • @ErwinBrandstetter I just don't like being categorical. In most of the cases there are better ways. And when I think about a use case, nothing comes to mind. But I wouldn't exclude a possibility of an application. E.g. if you're going to delete the database soon, there's probably no damage. If it's a development database, you can most likely recreate it from scratch. Just **be sure** to understand that the database may become **unusable**. – x-yuri Feb 21 '22 at 17:31
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.

Monolo
  • 18,205
  • 17
  • 69
  • 103
Tometzky
  • 22,573
  • 5
  • 59
  • 73
  • This works quite well with 9.3. Note that if you want to import into a new database you can change the three lines in the dump file that refer to the database name. – Gerry Shaw May 30 '14 at 23:03
11

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.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 3
    "The idea of allowing re-ordering of column position is not one the postgresql developers are against, it is more a case where no one has stepped forward to do the work." quote from http://wiki.postgresql.org/wiki/Alter_column_position In case someone wants to add that functionality... the starting steps have been outlined at the end of the article. – knowledge_is_power Sep 16 '11 at 10:13
  • 4
    +1 on that, although everyone I think is aware of it. From Igor Neyman's response, some seem not to have a reason for it. I think there often are situations when this feature is useful, esp. for OCD-in-schema people such as myself. As Tom Lane pointed out, we'd need to also change "views, foreign keys, indexes, defaults, rules, etc." and all other things dependent on columns. That actually doesn't seem so unimaginable, even just using PL/pgSQL. I think most of us stumbling upon this problem are feeling like "if only I had the time." – Greg Kramida Apr 25 '12 at 18:40
  • 1
    Thanks for the hint that it is still true for Postgres 11 (and later i guess) – leole Nov 12 '19 at 10:06
  • 1
    @leole: Yes, still true. – Erwin Brandstetter Nov 12 '19 at 15:02
6

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;
Turgs
  • 1,729
  • 1
  • 20
  • 49
4

Reorder the columns in postgresql walkthrough

Warning: this approach deletes table properties such as unique indexes and other unintended consequences that come with doing a drop your_table. So you'll need to add those back on after.

--create a table where column bar comes before column baz:
CREATE TABLE foo ( moo integer, bar character varying(10), baz date ); 

--insert some data
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; 

--observe the reordered columns:
select * from foo;
    ┌─────┬────────────┬───────┐ 
    │ moo │    baz     │  bar  │ 
    ├─────┼────────────┼───────┤ 
    │  34 │ 2021-04-07 │ yadz  │ 
    │  12 │ 2021-04-07 │ blerp │ 
    └─────┴────────────┴───────┘ 

Get the prior order of column names for copying and pasting

If your table you want to reorder has hundreds of columns, you'll want to automate the getting of the given order of columns so you can copy, nudge, then paste into the above views.

SELECT string_agg(column_name, ',') from ( 
    select * FROM INFORMATION_SCHEMA.COLUMNS  
    WHERE table_name = 'your_big_table'  
    order by ordinal_position asc 
) f1;

Which prints:

column_name_1,column_name_2, ..., column_name_n

You copy the above named ordering, you move them to where they belong then paste into the view up top.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
  • 2
    That's not changing the order of the columns, that's changing the order of the data based on a column. – rjmunro May 28 '14 at 08:47
  • I think you're on the right track with a view though. You can reorganize the columns of the table using a view. You can then either just use the view or create a new table from the view. A new table would be missing the indexes and keys and would need to be considered. https://stackoverflow.com/questions/285733/how-do-i-alter-the-position-of-a-column-in-a-postgresql-database-table/21028894#21028894 – Ken May 22 '18 at 15:34
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;
Alex Willison
  • 257
  • 7
  • 20
0

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

Nick Johnson
  • 100,655
  • 16
  • 128
  • 198
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=#
Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • I'm not writing a query. I'm using a product that auto-generates queries and forms and stuff. I can edit it after it has been generated, but I'd rather be able to regenerate and not have to edit again. – rjmunro Sep 24 '08 at 10:52
  • Then ask the product provider about a way to specify the column ordering (or ask they add it as a feature) – Vinko Vrsalovic Sep 24 '08 at 11:02
  • IIRC Postgresql developers don't want to add this feature :( – grom Sep 24 '08 at 11:41