如何将 table ID 从 serial 更改为 identity?
How to change a table ID from serial to identity?
我在 Postgres 10.10 中有以下 table:
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
。
table 有 107 条手动输入的记录,包括 clientids。
如何在不破坏现有数据的情况下做到这一点?
你可以修改定义,语法是:
ALTER TABLE table_name
ALTER COLUMN column_name
{ SET GENERATED { ALWAYS| BY DEFAULT } |
SET sequence_option | RESTART [ [ WITH ] restart ] }
不确定是否需要先更改 SET DEFAULT NULL
的列。并确保序列超过手动插入的值,因此没有冲突。
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;
A serial
列是一个 integer
列,它 拥有 一个专用序列并且默认设置为从中提取(从您发布的 table 定义)。要使其成为普通 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
列。它重用现有的序列,而不是直接更新系统目录——你不应该自己做,除非你确切地知道你在做什么。它还涵盖了异国情调的角落案例。看看(第"Upgrading"章):
但是,该功能不适用于大多数不允许直接操作系统目录的托管服务。然后您将按照顶部的说明返回 DDL 命令。
相关:
How to convert primary key from integer to serial?
Auto increment table column
How to copy structure and contents of a table, but with separate sequence?
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%';
此查询的结果可帮助您将所有序列号替换为生成的身份
我对脚本进行了一些更改 使其适用于我
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%';
我在 Postgres 10.10 中有以下 table:
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
。
table 有 107 条手动输入的记录,包括 clientids。
如何在不破坏现有数据的情况下做到这一点?
你可以修改定义,语法是:
ALTER TABLE table_name
ALTER COLUMN column_name
{ SET GENERATED { ALWAYS| BY DEFAULT } |
SET sequence_option | RESTART [ [ WITH ] restart ] }
不确定是否需要先更改 SET DEFAULT NULL
的列。并确保序列超过手动插入的值,因此没有冲突。
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;
A serial
列是一个 integer
列,它 拥有 一个专用序列并且默认设置为从中提取(从您发布的 table 定义)。要使其成为普通 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
列。它重用现有的序列,而不是直接更新系统目录——你不应该自己做,除非你确切地知道你在做什么。它还涵盖了异国情调的角落案例。看看(第"Upgrading"章):
但是,该功能不适用于大多数不允许直接操作系统目录的托管服务。然后您将按照顶部的说明返回 DDL 命令。
相关:
How to convert primary key from integer to serial?
Auto increment table column
How to copy structure and contents of a table, but with separate sequence?
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%';
此查询的结果可帮助您将所有序列号替换为生成的身份
我对脚本进行了一些更改
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%';