postgreSQL创建表分区
1.范围分区介绍
范围分区是根据某个列的范围来进行分区。常用于日期、时间或其他连续值类型的列。例如,按日期字段对表进行分区。
2.创建表
创建分区时使用的复合主键,所以在代码中如果集成了mybatisplus,不要使用selectById等类似方法,而是使用QueryWrapper查询。
sql
CREATE TABLE "public"."car" (
"id" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"plate_no" varchar(255) COLLATE "pg_catalog"."default",
"create_time" timestamp(6),
"car_id" varchar(64) COLLATE "pg_catalog"."default",
"height" varchar(64) COLLATE "pg_catalog"."default",
CONSTRAINT "car_partition_pkey" PRIMARY KEY ("id","create_time")
) PARTITION BY RANGE (
"create_time" "pg_catalog"."timestamp_ops"
)
;
3.每月创建分区函数
sql
CREATE OR REPLACE FUNCTION "public"."monthly_partition"()
RETURNS "pg_catalog"."void" AS $BODY$
DECLARE
start_date TIMESTAMP;
end_date TIMESTAMP;
partition_name TEXT;
partition_date TEXT;
BEGIN
-- 获取当前日期的下个月的第一天
start_date := date_trunc('month', CURRENT_DATE) + INTERVAL '1 month';
-- 获取下个月的最后一天
end_date := start_date + INTERVAL '1 month';
-- 动态生成car分区表的名称
partition_name := 'car_' || TO_CHAR(start_date, 'YYYY_MM');
-- 创建car分区表
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I PARTITION OF car FOR VALUES FROM (%L) TO (%L)',
partition_name, start_date, end_date
);
-- 动态生成car分区表的名称
partition_name := 'car_' || TO_CHAR(start_date, 'YYYY_MM');
-- 创建car分区表
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I PARTITION OF car FOR VALUES FROM (%L) TO (%L)',
partition_name, start_date, end_date
);
-- 为分区动态创建索引
EXECUTE format(
'CREATE INDEX IF NOT EXISTS plate_no_car_id_idx_%s ON %I (
plate_no,
car_id
)',
partition_date, partition_name
);
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
4.调用函数
在数据库中执行一次
sql
SELECT monthly_partition();
注:如果每月创建一个分区,可以通过定时任务调用数据库函数。(通过定时任务调用,每月执行一次)
在mapper中定义方法
java
String createMonthlyPartition();
然后在xml中编写sql
xml
<select id="createMonthlyPartition" statementType="CALLABLE" resultType="java.lang.String" >
{call monthly_partition()}
</select>
5.历史数据按月创建分区
如果旧表没有创建分区,重新建表分区后,必须要根据时间字段创建分区后才能导入数据。创建分区可以执行下面脚本。(只需执行一次)
sql
DO $$
DECLARE
start_date DATE := '2024-01-01'; -- 起始日期,建议设置为月份的第一天
current_date DATE := CURRENT_DATE; -- 当前日期
partition_name TEXT;
start_time TIMESTAMP;
end_time TIMESTAMP;
d DATE; -- 声明变量 d 为 DATE 类型
BEGIN
-- 使用 generate_series 生成从 start_date 到 current_date 的月份范围
FOR d IN
SELECT date_trunc('month', generate_series(start_date, current_date, '1 month'::INTERVAL))
LOOP
-- 为每个月生成对应的时间范围
start_time := d;
end_time := d + INTERVAL '1 month';
-- 动态生成分区名称
partition_name := 'car_' || TO_CHAR(d, 'YYYY_MM');
-- 创建分区
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I PARTITION OF car
FOR VALUES FROM (%L) TO (%L)',
partition_name, start_time, end_time
);
END LOOP;
END $$;
6.查看、删除分区索引
查看分区索引
sql
SELECT * FROM pg_indexes WHERE tablename = 'car_2025_01';
删除分区索引。plate_no_car_id_idx_为索引名
sql
DROP INDEX IF EXISTS plate_no_car_id_idx_;