如何将表 ID 从序列号更改为标识号?

Chr*_*ick 5 sql postgresql ddl auto-increment

我在 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)

Run Code Online (Sandbox Code Playgroud)

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

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

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

Erw*_*ter 9

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;
Run Code Online (Sandbox Code Playgroud)

有两个变量:

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

此查询同时获取:

SELECT pg_get_serial_sequence('client', 'clientid'), max(clientid) FROM client;
Run Code Online (Sandbox Code Playgroud)

serial列是integer该列拥有一个专门的序列,并有其默认设置从中得出(这可以从您发布的表定义可以看出)。为了使它成为一个普通的integer,删除默认值,然后删除序列。

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

ERROR:  more than one owned sequence found
Run Code Online (Sandbox Code Playgroud)

如何复制表的结构和内容,但顺序不同?

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

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

您的专栏以前是 PK 并保持 PK。这与变化是正交的。

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

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

有关的:

  • “找到多个拥有的序列”错误应该在 v12 中消失,并带有 [commit 19781729f78](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=19781729f789f3c6b2540e02b96f8aa500460322)。 (2认同)
  • 您的回答始终非常好,谢谢。 (2认同)