199

I've tried the following, but I was unsuccessful:

ALTER TABLE person ALTER COLUMN dob POSITION 37;
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Huuuze
  • 15,528
  • 25
  • 72
  • 91
  • 6
    This question was answered [before](http://stackoverflow.com/questions/126430/is-it-possible-to-change-the-natural-order-of-columns-in-postgres) – Tometzky Nov 13 '08 at 08:55
  • 2
    Possible duplicate of [Is it possible to change the natural order of columns in Postgres?](https://stackoverflow.com/questions/126430/is-it-possible-to-change-the-natural-order-of-columns-in-postgres) – peterh Aug 12 '17 at 02:49

11 Answers11

186

"Alter column position" in the PostgreSQL Wiki says:

PostgreSQL currently defines column order based on the attnum column of the pg_attribute table. The only way to change column order is either by recreating the table, or by adding columns and rotating data until you reach the desired layout.

That's pretty weak, but in their defense, in standard SQL, there is no solution for repositioning a column either. Database brands that support changing the ordinal position of a column are defining an extension to SQL syntax.

One other idea occurs to me: you can define a VIEW that specifies the order of columns how you like it, without changing the physical position of the column in the base table.

Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • 4
    'dump the database' :: great way to damage data. and hence "scrub the massive database" effect. – Kent Fredric Nov 13 '08 at 00:36
  • 49
    @Kent: Doubtful, the postgres devs go a long way to ensure data consistency and that would certainly apply to pg_dump. After all, what's the point of doing db backups if the restore is borked? – Dana the Sane Nov 13 '08 at 23:41
  • 1
    @Dana, yes, but you're adding 1) a big process to dump, and then you drop, and then you have a big time consuming load. If you have a *seriously* massive database, which is usually the case with 37 columns, you're going to have risks with disk IO choking. – Kent Fredric Nov 14 '08 at 06:22
  • @DanatheSane these are *super old* comments, but what Bill was saying is essentially the same thing, instead of doing a full database dump, you just dump the table (and dependencies), kill the original, then recreate. No need to take the whole database offline, or spend time re-creating something that's not affected. On the other hand, if there a lot of dependencies, I've found doing a full dump (as you suggested) to be much easier. – vol7ron Mar 07 '12 at 17:41
  • 1
    Here's why you might need column order - http://www.postgresql.org/docs/current/interactive/sql-copy.html - If a list of columns is specified, COPY will only copy the data in the specified columns to or from the file. If there are any columns in the table that are not in the column list, COPY FROM will insert the default values for those columns. – Nischal Bachu Jun 01 '15 at 13:37
  • @Nischal: Still only aesthetic, its still going to insert the default values, just in a different order. – Rahly Sep 10 '15 at 07:17
  • @Rahly It matters if you have all your scripts already set up to copy from some SQL files, but now you accidentally changed the column order. – sudo May 08 '17 at 18:30
  • @sudo Then you are doing something wrong, because I have tons of scripts. Changing the order of fields in actual SQL files seems dangerous. Reading from ANY kind of file blindly is dangerous and should always be taking into context, the context in this case is the order of the fields. From something like pg_dump or pg_dumpall, you should ALREADY know the order, from the file itself. – Rahly May 10 '17 at 01:09
  • @Rahly I do use `pg_dump` for backups. We have a script somewhere that's inserting from one table (originally from a backup) to another, and the tables are normally the same layout, so it's just inserting `*` so we don't have to edit that script every time the table layout changes. Just this one time, I had to drop/recreate a column in one. – sudo May 10 '17 at 01:54
  • @sudo then order shouldn't matter, and is still aesthetic. The dump contains the order of the fields. I never need to edit my scripts, because they read the order from the dump file. In fact, I can remove or add new fields and the script handles it just fine. – Rahly May 10 '17 at 02:07
  • I think create a view is just fine – deFreitas Jul 29 '18 at 15:32
  • please explain **rotate** solution of the suggestion *"or by ... rotating data until you reach the desired layout"* – Peter Krauss Sep 11 '18 at 14:21
  • @PeterKrauss, It's not my writing, so I can only guess. I think they mean: *Add new columns to the table, duplicating the current columns, in the order you want. Copy data from the old columns to the new columns. Then drop old columns.* I wouldn't bother with all that! – Bill Karwin Sep 11 '18 at 14:26
  • In MySQL: https://stackoverflow.com/questions/10718905/how-to-change-the-column-position-of-mysql-table-without-losing-column-data – Janac Meena Sep 30 '20 at 20:09
  • @JanacMeena, Yes, that works in MySQL, but the question here was specifically about PostgreSQL. – Bill Karwin Sep 30 '20 at 20:16
  • @BillKarwin, I understand that, however when Googling the solution for MySQL, this page happened to pop-up. So I figured I would add a link for other wanderers as myself. – Janac Meena Sep 30 '20 at 20:20
131

In PostgreSQL, while adding a field it would be added at the end of the table. If we need to insert into particular position then

    alter table tablename rename to oldtable;
    create table tablename (column defs go here); ### with all the constraints
    insert into tablename (col1, col2, col3) select col1, col2, col3 from oldtable;
Allwin
  • 2,060
  • 1
  • 17
  • 16
  • 6
    Wow this is great, I'm sure this should be the accepted answer! – Tommaso Thea Feb 28 '19 at 22:51
  • 6
    You can also copy the current table to a new oldtable like this: CREATE TABLE oldtable AS SELECT * FROM tablename; – Ryan Jul 24 '20 at 23:09
  • 1
    Works perfectly. It really should be the accepted answer! – juan Aug 20 '20 at 19:05
  • 2
    Here's an example with real world values: insert into newTable (id, created, start, end, message) SELECT id, created, start, end, message FROM oldTable; – Janac Meena Sep 30 '20 at 14:59
  • 9
    It is not that easy to move from one table to another as there are other objects related to them such us constraints. – thanos.a Mar 19 '21 at 08:27
  • 10
    I think this solution also messes up foreign keys. The `ALTER TABLE ... RENAME TO ...` seems to change existing foreign key relationships in other tables accordingly. That means that in the end foreign keys point to `oldtable` but the data is in the new table. I assume a manual migration of all foreign key relationships is required before one can properly drop `oldtable`. – bluenote10 Jun 23 '21 at 07:49
33

One, albeit a clumsy option to rearrange the columns when the column order must absolutely be changed, and foreign keys are in use, is to first dump the entire database with data, then dump just the schema (pg_dump -s databasename > databasename_schema.sql). Next edit the schema file to rearrange the columns as you would like, then recreate the database from the schema, and finally restore the data into the newly created database.

Ville
  • 4,088
  • 2
  • 37
  • 38
  • 4
    Why a downvote? As the accepted answer points out, quoting the PostgreSQL Wiki: »The only way to change column order is either by recreating the table, or by adding columns and rotating data until you reach the desired layout.» This solution is not optimal (none of these answers are in the absence of a built-in operation to accomplish the task), but it does provide a way to rearrange the columns in a table. – Ville Apr 27 '16 at 15:19
  • 4
    I say again, there are no good ways to do this, and what I outline above _is_ a valid way to rearrange columns if they absolutely must be rearranged. If you downvote my answer, please comment on why you feel it's an unacceptable solution. – Ville Jun 22 '17 at 06:32
  • 6
    In fact, this is a very good solution when the table is foreign key in another tables. All another solutions does not work in this escenario. Finally, use pg_dump --column-inserts -s databasename > databasename_schema.sql – Alejandro Salamanca Mazuelo Aug 11 '17 at 15:45
29

This post is old and probably solved but I had the same issue. I resolved it by creating a view of the original table specifying the new column order.

From here I could either use the view or create a new table from the view.

    CREATE VIEW original_tab_vw AS
    SELECT a.col1, a.col3, a.col4, a.col2
    FROM original_tab a
    WHERE a.col1 IS NOT NULL --or whatever
    SELECT * INTO new_table FROM original_tab_vw

Rename or drop the original table and set the name of the new table to the old table.

Ken
  • 474
  • 6
  • 8
12

I don't think you can at present: see this article on the Postgresql wiki.

The three workarounds from this article are:

  1. Recreate the table
  2. Add columns and move data
  3. Hide the differences with a view.
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
6

Open the table in PGAdmin and in the SQL pane at the bottom copy the SQL Create Table statement. Then open the Query Tool and paste. If the table has data, change the table name to 'new_name', if not, delete the comment "--" in the Drop Table line. Edit the column sequence as required. Mind the missing/superfluous comma in the last column in case you have moved it. Execute the new SQL Create Table command. Refresh and ... voilà.

For empty tables in the design stage this method is quite practical.

In case the table has data, we need to rearrange the column sequence of the data as well. This is easy: use INSERT to import the old table into its new version with:

INSERT INTO new ( c2, c3, c1 ) SELECT * from old;

... where c2, c3, c1 are the columns c1, c2, c3 of the old table in their new positions. Please note that in this case you must use a 'new' name for the edited 'old' table, or you will lose your data. In case the column names are many, long and/or complex use the same method as above to copy the new table structure into a text editor, and create the new column list there before copying it into the INSERT statement.

After checking that all is well, DROP the old table and change the the 'new' name to 'old' using ALTER TABLE new RENAME TO old; and you are done.

André C. Andersen
  • 8,955
  • 3
  • 53
  • 79
marcopolo
  • 123
  • 2
  • 6
4

I was working on re-ordering a lot of tables and didn't want to have to write the same queries over and over so I made a script to do it all for me. Essentially, it:

  1. Gets the table creation SQL from pg_dump
  2. Gets all available columns from the dump
  3. Puts the columns in the desired order
  4. Modifies the original pg_dump query to create a re-ordered table with data
  5. Drops old table
  6. Renames new table to match old table

It can be used by running the following simple command:

./reorder.py -n schema -d database table \
    first_col second_col ... penultimate_col ultimate_col --migrate

It prints out the sql so you can verify and test it, that was a big reason I based it on pg_dump. You can find the github repo here.

GammaGames
  • 1,617
  • 1
  • 17
  • 32
1

I use Django and it requires id column in each table if you don't want to have a headache. Unfortunately, I was careless and my table bp.geo_location_vague didn't contain this field. I initialed little trick. Step 1:

CREATE VIEW bp.geo_location_vague_vw AS
    SELECT 
        a.id, -- I change order of id column here. 
        a.in_date,
        etc
    FROM bp.geo_location_vague a

Step 2: (without create table - table will create automaticaly!)

SELECT * into bp.geo_location_vague_cp2 FROM bp.geo_location_vague_vw

Step 3:

CREATE SEQUENCE bp.tbl_tbl_id_seq;
ALTER TABLE bp.geo_location_vague_cp2 ALTER COLUMN id SET DEFAULT nextval('tbl_tbl_id_seq');
ALTER SEQUENCE bp.tbl_tbl_id_seq OWNED BY bp.geo_location_vague_cp2.id;
SELECT setval('tbl_tbl_id_seq', COALESCE(max(id), 0)) FROM bp.geo_location_vague_cp2;

Because I need have bigserial pseudotype in the table. After SELECT * into pg will create bigint type insetad bigserial.

step 4: Now we can drop the view, drop source table and rename the new table in the old name. The trick was ended successfully.

Orlov Const
  • 332
  • 3
  • 10
1

For those tempted to change column order like this, just know it won't work because whole table gets messed up. You'll receive an error like this: [XX000] ERROR: invalid memory alloc request size 18446744073709551613

Unfortunately, it seems like the attnum is not only used for retrieval of data but for storage as well.

The idea to drop whole table is all good and fine but you also have to drop all FKs, IXs and so on. I'll probably learn to live with my column being at back.

select *
from information_schema.columns
where table_name = 'table1';

update pg_catalog.pg_attribute
set attnum = 10 where attname = 'column_on_9_position_to_move_to_7';

update pg_catalog.pg_attribute
set attnum = 9 where attname = 'column_on_7_position_to_move_to_9';

update pg_catalog.pg_attribute
set attnum = 7 where attname = 'column_on_9_position_to_move_to_7';
0

There are some workarounds to make it possible:

  1. Recreating the whole table

  2. Create new columns within the current table

  3. Create a view

https://tableplus.com/blog/2018/09/postgresql-is-it-possible-to-alter-column-order-position-in-a-table.html

0

I made a backup of my database's data structure in PLAIN format, and edited the column order in NotePad, then created a new database and ran all the code in a query. It worked 100%

starball
  • 20,030
  • 7
  • 43
  • 238