PostgreSQL遍历所有的表并设置id为自增主键(基于自建函数)
创建自定义函数
-- DROP FUNCTION public.create_seq_and_set_pkey(text);
CREATE OR REPLACE FUNCTION public.create_seq_and_set_pkey(histroy_table_name text)
RETURNS void
LANGUAGE plpgsql
NOT FENCED NOT SHIPPABLE
AS $function$
DECLARE
max_id BIGINT;
sequence_name TEXT;
BEGIN
-- 获取当前表的最大id
EXECUTE FORMAT('
SELECT COALESCE(MAX(id), 0)
FROM %I',
histroy_table_name
) INTO max_id;
RAISE NOTICE 'Max ID for table % is %', histroy_table_name, max_id;
-- 生成序列名称
sequence_name := histroy_table_name || '_id_seq';
RAISE NOTICE 'Sequence name for table % is %', histroy_table_name, sequence_name;
-- 检查序列是否存在
IF EXISTS (SELECT 1 FROM pg_class WHERE relkind = 'S' AND relname = sequence_name) THEN
-- 如果序列存在,则修改序列的起始值为 max_id + 1
EXECUTE FORMAT('
SELECT setval(%L, %s)',
sequence_name,
max_id + 1
);
RAISE NOTICE 'Updated sequence % to start from %', sequence_name, max_id + 1;
ELSE
-- 如果序列不存在,则创建序列并设置起始值为 1
EXECUTE FORMAT('
CREATE SEQUENCE %I START %s',
sequence_name,
1
);
RAISE NOTICE 'Created sequence % starting from 1', sequence_name;
END IF;
-- 检查 id 列是否已设置默认值,如果没有则设置默认值
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = histroy_table_name AND column_name = 'id' AND column_default IS NOT NULL) THEN
EXECUTE FORMAT('
ALTER TABLE %I
ALTER COLUMN id SET DEFAULT nextval(%L)',
histroy_table_name,
sequence_name
);
RAISE NOTICE 'Set default value for id column in table % to nextval(%L)', histroy_table_name, sequence_name;
ELSE
RAISE NOTICE 'Default value for id column in table % already set', histroy_table_name;
END IF;
-- 检查主键是否已存在,如果不存在则添加主键
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE table_name = histroy_table_name AND constraint_type = 'PRIMARY KEY') THEN
EXECUTE FORMAT('
ALTER TABLE %I
ADD PRIMARY KEY (id)',
histroy_table_name
);
RAISE NOTICE 'Added primary key to table %', histroy_table_name;
ELSE
RAISE NOTICE 'Primary key already exists in table %', histroy_table_name;
END IF;
END;
$function$;
单独执行示例
select create_seq_and_set_pkey('表名');
遍历执行
使用方式:数据库命令行直接运行
DO $$
DECLARE
table_name TEXT;
BEGIN
-- 遍历 public 模式下的所有表
FOR table_name IN (
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
order by tablename
)
LOOP
-- 调用函数 create_seq_and_set_pkey
PERFORM create_seq_and_set_pkey(table_name);
END LOOP;
END;
$$ LANGUAGE plpgsql;
遍历 public
模式下的所有表,并对每个表调用名为 create_seq_and_set_pkey
的函数。以下是对代码的详细解释:
-
声明部分 (
DECLARE
):-
table_name TEXT;
:声明一个变量table_name
,用于存储表名。
-
-
BEGIN 和 END 之间的主体部分:
-
遍历表 (
FOR
循环):-
FOR table_name IN (...)
:使用FOR
循环遍历public
模式下的所有表。 -
SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename
:从pg_tables
系统表中选择public
模式下的所有表名,并按表名排序。
-
-
调用函数
create_seq_and_set_pkey
:-
PERFORM create_seq_and_set_pkey(table_name);
:对当前表名调用create_seq_and_set_pkey
函数。PERFORM
语句用于执行函数并忽略其返回值。
-
-
整个存储过程的作用是遍历 public
模式下的所有表,并对每个表调用 create_seq_and_set_pkey
函数。
自定义函数剖析
create_seq_and_set_pkey 函数的功能是确保每个表具有一个序列,并且
id
列使用该序列作为默认值,同时确保表具有主键约束。函数接受一个表名作为参数,并执行以下操作:
-
获取当前表的最大 ID:
-
使用
EXECUTE FORMAT
语句动态执行 SQL 查询,获取表中的最大id
值,并将结果存储在变量max_id
中。 -
RAISE NOTICE 'Max ID for table % is %', histroy_table_name, max_id;
输出日志信息,显示表的最大 ID。
-
-
生成序列名称:
-
将表名与
_id_seq
拼接,生成序列名称,并存储在变量sequence_name
中。 -
RAISE NOTICE 'Sequence name for table % is %', histroy_table_name, sequence_name;
输出日志信息,显示生成的序列名称。
-
-
检查序列是否存在:
-
使用
IF EXISTS
语句检查序列是否已经存在。 -
如果序列存在,则使用
EXECUTE FORMAT
语句修改序列的起始值为max_id + 1
。 -
RAISE NOTICE 'Updated sequence % to start from %', sequence_name, max_id + 1;
输出日志信息,显示更新后的序列起始值。 -
如果序列不存在,则使用
EXECUTE FORMAT
语句创建序列,并设置起始值为 1。 -
RAISE NOTICE 'Created sequence % starting from 1', sequence_name;
输出日志信息,显示创建的序列起始值。
-
-
检查
id
列是否已设置默认值:-
使用
IF NOT EXISTS
语句检查id
列是否已设置默认值。 -
如果没有设置默认值,则使用
EXECUTE FORMAT
语句设置id
列的默认值为nextval(sequence_name)
。 -
RAISE NOTICE 'Set default value for id column in table % to nextval(%L)', histroy_table_name, sequence_name;
输出日志信息,显示设置的默认值。 -
如果已设置默认值,则输出日志信息,显示默认值已存在。
-
-
检查主键是否已存在:
-
使用
IF NOT EXISTS
语句检查主键是否已存在。 -
如果不存在主键,则使用
EXECUTE FORMAT
语句添加主键约束。 -
RAISE NOTICE 'Added primary key to table %', histroy_table_name;
输出日志信息,显示添加的主键。 -
如果主键已存在,则输出日志信息,显示主键已存在。
-