【问题标题】:How to change a table ID from serial to identity?如何将表 ID 从串行更改为身份?
【发布时间】:2020-04-01 14:30:48
【问题描述】:

我在 Postgres 10.10 中有下表:

  Table "public.client"
       Column        |  Type   | Collation | Nullable |                 Default                  
---------------------+---------+-----------+----------+------------------------------------------
 clientid            | integer |           | not null | nextval('client_clientid_seq'::regclass)
 account_name        | text    |           | not null | 
 last_name           | text    |           |          | 
 first_name          | text    |           |          | 
 address             | text    |           | not null | 
 suburbid            | integer |           |          | 
 cityid              | integer |           |          | 
 post_code           | integer |           | not null | 
 business_phone      | text    |           |          | 
 home_phone          | text    |           |          | 
 mobile_phone        | text    |           |          | 
 alternative_phone   | text    |           |          | 
 email               | text    |           |          | 
 quote_detailsid     | integer |           |          | 
 invoice_typeid      | integer |           |          | 
 payment_typeid      | integer |           |          | 
 job_typeid          | integer |           |          | 
 communicationid     | integer |           |          | 
 accessid            | integer |           |          | 
 difficulty_levelid  | integer |           |          | 
 current_lawn_price  | numeric |           |          | 
 square_meters       | numeric |           |          | 
 note                | text    |           |          | 
 client_statusid     | integer |           |          | 
 reason_for_statusid | integer |           |          | 
Indexes:
    "client_pkey" PRIMARY KEY, btree (clientid)
    "account_name_check" UNIQUE CONSTRAINT, btree (account_name)
Foreign-key constraints:
    "client_accessid_fkey" FOREIGN KEY (accessid) REFERENCES access(accessid)
    "client_cityid_fkey" FOREIGN KEY (cityid) REFERENCES city(cityid)
    "client_client_statusid_fkey" FOREIGN KEY (client_statusid) REFERENCES client_status(client_statusid)
    "client_communicationid_fkey" FOREIGN KEY (communicationid) REFERENCES communication(communicationid)
    "client_difficulty_levelid_fkey" FOREIGN KEY (difficulty_levelid) REFERENCES difficulty_level(difficulty_levelid)
    "client_invoice_typeid_fkey" FOREIGN KEY (invoice_typeid) REFERENCES invoice_type(invoice_typeid)
    "client_job_typeid_fkey" FOREIGN KEY (job_typeid) REFERENCES job_type(job_typeid)
    "client_payment_typeid_fkey" FOREIGN KEY (payment_typeid) REFERENCES payment_type(payment_typeid)
    "client_quote_detailsid_fkey" FOREIGN KEY (quote_detailsid) REFERENCES quote_details(quote_detailsid)
    "client_reason_for_statusid_fkey" FOREIGN KEY (reason_for_statusid) REFERENCES reason_for_status(reason_for_statusid)
    "client_suburbid_fkey" FOREIGN KEY (suburbid) REFERENCES suburb(suburbid)
Referenced by:
    TABLE "work" CONSTRAINT "work_clientid_fkey" FOREIGN KEY (clientid) REFERENCES client(clientid)

我想将clientid 从序列号 (nextval('client_clientid_seq'::regclass)) 更改为 not null generated always as identity primary key

该表有 107 条手动输入的记录,包括客户端 ID。

如何在不破坏现有数据的情况下做到这一点?

【问题讨论】:

  • 感谢您提供的所有信息。当场就成功了。

标签: sql postgresql auto-increment ddl


【解决方案1】:

我对脚本 posted by Madina 进行了一些更改,使其能够为我工作

SELECT 'ALTER TABLE '||table_schema||'."'||TABLE_NAME||'" ALTER '||COLUMN_NAME||' DROP DEFAULT;
'||replace('DROP SEQUENCE '''||substring(column_default, 9, length(column_default)-19), '''', '')||';
ALTER TABLE  '||table_schema||'."'||TABLE_NAME||'" ALTER '||COLUMN_NAME||' ADD GENERATED ALWAYS AS IDENTITY;
SELECT SETVAL(pg_get_serial_sequence('''||table_schema||'.'||TABLE_NAME||''', '''||COLUMN_NAME||'''),
(SELECT COALESCE(MAX('||COLUMN_NAME||'), 0) + 1 FROM '||table_schema||'.'||TABLE_NAME||'), false);'
FROM information_schema.columns
WHERE column_default LIKE 'nextval%';

【讨论】:

    【解决方案2】:
    SELECT 'ALTER TABLE '||table_schema||'."'||TABLE_NAME||'" ALTER '||COLUMN_NAME||' DROP DEFAULT;
    '||replace('DROP SEQUENCE '''||substring(column_default, 9, length(column_default)-19), '''', '')||'  CASCADE;
    ALTER TABLE  '||table_schema||'."'||TABLE_NAME||'" ALTER COLUMN '||COLUMN_NAME||' set not null;
    ALTER TABLE  '||table_schema||'."'||TABLE_NAME||'" ALTER '||COLUMN_NAME||' ADD GENERATED ALWAYS AS IDENTITY;
    SELECT setval(pg_get_serial_sequence(''"'||TABLE_NAME||'"'', '''||COLUMN_NAME||'''),
    (select max('||COLUMN_NAME||') from '||table_schema||'."'||TABLE_NAME||'"));'
    FROM information_schema.columns
    WHERE column_default LIKE 'nextval%';
    

    此查询的结果可以帮助您将所有序列号替换为生成的身份

    【讨论】:

      【解决方案3】:
      BEGIN;
      ALTER TABLE public.client ALTER clientid DROP DEFAULT; -- drop default
      
      DROP SEQUENCE public.client_clientid_seq;              -- drop owned sequence
      
      ALTER TABLE public.client
      -- ALTER clientid SET DATA TYPE int,                   -- not needed: already int
         ALTER clientid ADD GENERATED ALWAYS AS IDENTITY (RESTART 108);
      COMMIT;
      

      有两个变量:

      • 所附SEQUENCE 的实际名称。我使用了上面的默认名称,但名称可以不同。
      • client.clientid 中的当前最大值。不必是 107,只是因为当前有 107 行。

      这个查询得到两个:

      SELECT pg_get_serial_sequence('client', 'clientid'), max(clientid) FROM client;
      

      serial 列是一个integer 列,拥有一个专用序列,并且默认设置为从中提取(从您发布的表定义中可以看出)。要使其成为普通的 integer,请删除默认值,然后删除序列。

      将列转换为IDENTITY 会添加它自己的序列。您必须删除旧拥有的序列(或至少所有权,随着删除序列而死)。否则你会得到如下错误:

      ERROR:  more than one owned sequence found
      

      How to copy structure and contents of a table, but with separate sequence?

      然后将普通的integer 列转换为IDENTITY 列,并以当前最大值加1重新开始。您必须设置新内部序列的当前值以避免唯一违规。

      将所有内容封装在一个事务中,这样您就不会在迁移过程中搞砸了。所有这些 DDL 命令在 Postgres 中都是事务性的,可以回滚直到提交,并且只对之后开始的其他事务可见。

      您的专栏之前是 PK 并保持 PK。这与更改正交。

      Peter Eisentraut,(Postgres 10 中的新功能)IDENTITY 功能的主要作者,还提供了一个函数upgrade_serial_to_identity() 来转换现有的serial 列。它重用现有序列,而是直接更新系统目录 - 除非您确切知道自己在做什么,否则您不应该自己做。它还涵盖了异国情调的角落案例。看看(“升级”一章):

      但是,该功能不适用于大多数不允许直接操作系统目录的托管服务。然后按照顶部的说明返回 DDL 命令。

      相关:

      【讨论】:

      • more than one owned sequence found 错误应该在 v12 中通过 commit 19781729f78 消失。
      【解决方案4】:

      你可以改变定义,语法是:

      ALTER TABLE table_name 
      ALTER COLUMN column_name 
      { SET GENERATED { ALWAYS| BY DEFAULT } | 
        SET sequence_option | RESTART [ [ WITH ] restart ] }
      

      不确定是否需要先更改带有SET DEFAULT NULL 的列。并确保序列超过手动插入的值,因此没有冲突。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-01-04
        • 1970-01-01
        • 2014-04-28
        • 1970-01-01
        • 2016-03-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多