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

后端 未结 8 1715
既然无缘
既然无缘 2020-12-05 13:25

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 do

相关标签:
8条回答
  • 2020-12-05 13:48

    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;
    
    0 讨论(0)
  • 2020-12-05 13:54

    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;
    
    0 讨论(0)
  • 2020-12-05 14:01

    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.

    0 讨论(0)
  • 2020-12-05 14:02

    As the other answers mentioned, you cannot change the order of columns, that's up to postgres. You can (and should!) 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;
    
    0 讨论(0)
  • 2020-12-05 14:03

    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=#
    
    0 讨论(0)
  • 2020-12-05 14:10

    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.

    0 讨论(0)
提交回复
热议问题