PostgreSQL 批量创建序列并使用序列

迁移生产数据库过程中发现问题,PG多张表公用一个序列,实际上如果表中数据每日变动量较小的话理论上是可行的。

但是如果太大就考虑需要单独为每张表建立一个序列,并使其id自增。

不过确实没有mysql方便可以直接设置“自增主键”,每次创建序列确实麻烦。

于是...

批量创建序列

注意执行用户权限 没有权限可能会查不到数据

- -如果需要按表名动态更改序列名称,可以使用动态SQL语句中的变量来构造序列名。以下是一个示例,用于为数据库中的每个表创建名为"table_name_id_seq"的序列,其中"table_name"是表名:

-- 在这个示例中,我们使用了pg_tables元数据表来获取所有test架构下的表名。你可以根据需要修改条件。

-- 执行此代码段将为数据库中的每个表创建一个名为"table_name_id_seq"的序列,其

中"table_name"是表名,  table_schema 表示你的模式名。

DO $$DECLARE
  table_name varchar(255);
  sql_statement varchar(1000);
BEGIN
  FOR table_name IN SELECT tablename FROM pg_tables WHERE schemaname='test'
  LOOP
    sql_statement := 'CREATE SEQUENCE ' || table_name || '_id_seq START 1 INCREMENT 1';
    EXECUTE sql_statement;
  END LOOP;
END$$;

当然,这种做法适用于都是新表的情况下,一般我们的表中都会存在数据,所以我们需要保证我们的新序列的起始值是我们表中的最大值,这样才能保证后续的id不重复。

我们使用EXECUTE语句动态构建了查询最大ID或AutoID值的SQL语句,并使用EXECUTE语句执行该SQL语句,将查询结果存储到'max_id'变量中。这样,就可以正确解析'table_record.table_name'关系


DO $$
DECLARE
  table_record record;
  max_id bigint;
  sql_statement varchar(1000);
  sequence_name varchar(100);
BEGIN
  FOR table_record IN SELECT table_name, column_name
                      FROM information_schema.columns
                      WHERE table_schema='test' AND (column_name = 'autoid' OR column_name = 'id')
  LOOP
    sequence_name := table_record.table_name || '_id_seq';
    IF NOT EXISTS (SELECT 1 FROM pg_class WHERE relname = sequence_name ) THEN
      EXECUTE 'SELECT COALESCE(MAX(' || table_record.column_name || '), 0) FROM ' || table_record.table_name INTO max_id;
      sql_statement := 'CREATE SEQUENCE ' || sequence_name || ' START ' || max_id + 1 || ' INCREMENT 1';
      EXECUTE sql_statement;
    END IF;
  END LOOP;
END$$;

批量使用序列

-- 要为每个表的id或auto id列使用对应的序列,可以使用以下示例代码:

-- 在这个示例中,我们使用了information_schema.columns元数据表来获取所有具有'id'或'auto_id'列的表名和列名。你可以根据需要修改条件。

-- 执行此代码段将为每个具有'id'或'auto_id'列的表创建一个名为"table_name_id_seq"的序列,如果存在对应关系,执行跳过。并将每个表的id或auto id列的默认值设置为该序列的下一个值。

DO $$
DECLARE
  table_rec RECORD;
  tablename varchar(255);
  col_name varchar(255);
  sql_statement varchar(1000);
	bool_result BOOLEAN;
BEGIN
  FOR table_rec IN (SELECT table_name, column_name
                    FROM information_schema.columns
                    WHERE table_schema='test' AND (column_name = 'autoid' OR column_name = 'id'))
  LOOP
    tablename := table_rec.table_name;
    col_name := table_rec.column_name;
    sql_statement := 'SELECT EXISTS(SELECT 1 FROM information_schema.sequences WHERE sequence_name = ''' || tablename || '_id_seq'' and sequence_schema = ''iotms'')';
    EXECUTE sql_statement INTO bool_result;
    IF bool_result THEN
      sql_statement := 'ALTER TABLE ' || tablename || ' ALTER COLUMN ' || col_name || ' SET DEFAULT nextval(''' || tablename || '_id_seq''::regclass)';
      EXECUTE sql_statement;
    END IF;
  END LOOP;
END$$;

辅助sql,可以用来验证具体某一序列

-- 授权所有序列给指定用户
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA iotms TO user_iotms

-- 查看序列有哪些表使用
SELECT * from information_schema.sequences WHERE sequence_name = 'devicegroup'

-- 查询出所有表
SELECT TABLENAME from pg_tables WHERE SCHEMANAME = 'test'
-- 查询该模式表下的主键列名称
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_schema='test' AND (column_name = 'autoid' OR column_name = 'id')
-- 查询每张表下的主键最大值
SELECT COALESCE(MAX( autoid), 0) as maxid FROM yzz_iot_device_branch_20230316
-- 查询序列的当前最大值
select LAST_VALUE from dp_dict_id_seq

了解一下这种写法:

这种写法是一种叫做PL/pgSQL的编程语言,它是PostgreSQL内置的过程化语言。而这段代码则是一个匿名代码块,也叫做"匿名存储过程",它包含了一些SQL语句和PL/pgSQL代码,可以被直接执行。因此,我们可以将其看作是一种存储过程的变体。

在这段代码中,我们使用了DECLARE关键字来声明变量,用于存储查询元数据表后的结果。然后,我们使用FOR循环遍历查询结果,并对每个表的'id'或'auto_id'列执行ALTER TABLE语句,以将其默认值设置为该列对应序列的下一个值。在代码块的末尾,我们使用END关键字来结束代码块。

需要注意的是,由于这段代码是一个匿名代码块,它不会在数据库中创建任何对象。因此,它只适用于一次性的、临时性的任务,而不能像存储过程一样被重复调用。

总之,这段代码使用了PL/pgSQL的语法结构,它类似于存储过程,但并不是一个真正的存储过程。

你可能感兴趣的:(后端,数据库,postgresql,sql)