Skip to content

范围分区(RANGE) ​

范围分区(Range Partitioning)是声明式分区里最常用、收益也最直接的一种:它把分区键的取值切成若干段互不重叠的左闭右开区间,每一段对应一个分区。因为绝大多数业务查询都带时间范围条件,范围分区几乎总能配合分区裁剪(Partition Pruning)把扫描范围压到一个或少数几个分区上。

本节讲清 FROM (...) TO (...) 的边界语义、MINVALUE / MAXVALUE 的正确用法、date_trunc 对齐、多列范围键与 DEFAULT 分区,最后给出「插入报错」这类问题的完整排查路径。

前置知识

阅读本节前,建议先了解:声明式分区概述

基础概念 ​

左闭右开:范围分区唯一需要牢记的语义 ​

官方定义很明确:每个范围的下界是包含的(inclusive),上界是排他的(exclusive)。即:

text
partition_bound:  [ lower , upper )

所以 [1, 10) 与 [10, 20) 是相邻且不重叠的,值 10 属于第二个分区。

text
      [1,10)        [10,20)        [20,30)
  -----+-------------+-------------+----->
       1             10            20    30
       └── 属于分区1 ─┘└── 属于分区2 ─┘
                     ▲
                  10 落在这里

这条规则带来两个非常重要的好处:

好处说明
相邻边界可以「共用同一个值」不需要写 TO ('2026-02-01') 和 FROM ('2026-02-02') 这种别扭写法
无缝衔接,无空洞月度分区只需 [01-01, 02-01)、[02-01, 03-01) 依次排开

分区边界的四种形式 ​

text
partition_bound_spec is:

IN ( partition_bound_expr [, ...] ) |
FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
 TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )

其中 FROM ... TO ... 用于 RANGE,IN (...) 用于 LIST,WITH (...) 用于 HASH。DEFAULT 则是一个独立的关键字形式(用于给父表补一个「兜底」分区)。

MINVALUE 与 MAXVALUE ​

MINVALUE / MAXVALUE 表示「该类型的最小值 / 最大值」,用于表达「无下界」和「无上界」:

写法含义
FROM (MINVALUE) TO ('2026-01-01')所有小于 2026-01-01 的值(历史归档区)
FROM ('2026-01-01') TO (MAXVALUE)所有大于等于 2026-01-01 的值(无上界)
FROM (MINVALUE) TO (MAXVALUE)全量,一般不这么写(等于没分区)
sql
-- 历史归档分区 + 无上界的「当前区」
CREATE TABLE logs_old  PARTITION OF logs FOR VALUES FROM (MINVALUE) TO ('2026-01-01');
CREATE TABLE logs_recent PARTITION OF logs FOR VALUES FROM ('2026-01-01') TO (MAXVALUE);

MINVALUE / MAXVALUE 不是语法糖,是真实边界

MAXVALUE 代表的是「比该类型任何合法值都大」的哨兵值,不是 NULL,也不是无穷。使用它之后,后续就无法再插入一个比它更大的分区——所以不建议用 TO (MAXVALUE) 长期承载新数据,而应预先按月建分区。

单列范围键 vs 多列范围键 ​

类型写法语义
单列PARTITION BY RANGE (created_at)按一列切段
多列PARTITION BY RANGE (tenant_id, created_at)字典序比较,先比第一列再比第二列

多列范围分区的边界也是元组比较:

sql
PARTITION BY RANGE (tenant_id, created_at)
-- 分区边界是元组
FOR VALUES FROM (1, '2026-01-01') TO (1, '2026-02-01')

常见用途一览 ​

场景分区粒度说明
订单 / 交易表月最经典,配合轮转删除
日志 / 事件表天或小时量极大,按天配合保留策略
时序指标表天常与 DEFAULT 或 MAXVALUE 搭配
按 ID 段归档固定区间如 [0, 10000000)、[10000000, 20000000)
多租户 + 时间租户 × 月两级或复合键

语法与用法 ​

text
-- 创建按范围分区的父表
CREATE TABLE table_name ( ... )
    PARTITION BY RANGE ( column_name [, ... ] )

-- 创建分区(左闭右开)
CREATE TABLE partition_name PARTITION OF parent
    FOR VALUES FROM ( lower_bound ) TO ( upper_bound )

-- 边界项可以是常量,也可以是 MINVALUE / MAXVALUE
CREATE TABLE partition_name PARTITION OF parent
    FOR VALUES FROM ( MINVALUE ) TO ( '2026-01-01' )

-- 兜底分区
CREATE TABLE partition_name PARTITION OF parent DEFAULT

-- 把已有表挂进来(带边界校验)
ALTER TABLE parent ATTACH PARTITION partition_name
    FOR VALUES FROM ( lower_bound ) TO ( upper_bound )

最简可运行示例:

sql
CREATE TABLE orders (
  id         bigint        NOT NULL,
  user_id    bigint        NOT NULL,
  amount     numeric(12,2) NOT NULL,
  created_at date          NOT NULL
) PARTITION BY RANGE (created_at);
text
CREATE TABLE
sql
CREATE TABLE orders_2026_01 PARTITION OF orders
  FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
text
CREATE TABLE

插入后按边界自动路由:

sql
INSERT INTO orders VALUES (1, 1001, 99.00, '2026-01-15');
text
INSERT 0 1
sql
SELECT tableoid::regclass AS partition FROM orders WHERE id = 1;
text
   partition
----------------
 orders_2026_01
(1 row)

用 psql 确认边界 ​

sql
\d+ orders
text
                       Partitioned table "public.orders"
   Column   |     Type      | Collation | Nullable | Default
------------+---------------+-----------+----------+---------
 id         | bigint        |           | not null |
 user_id    | bigint        |           | not null |
 amount     | numeric(12,2) |           | not null |
 created_at | date          |           | not null |
Partition key: RANGE (created_at)
Partitions: orders_2026_01 FOR VALUES FROM ('2026-01-01') TO ('2026-02-01')

也可以用目录函数拿到边界表达式:

sql
SELECT
  child.relname AS partition,
  pg_get_expr(child.relpartbound, child.oid) AS bound
FROM pg_inherits i
JOIN pg_class parent ON i.inhparent = parent.oid
JOIN pg_class child  ON i.inhrelid  = child.oid
WHERE parent.relname = 'orders'
ORDER BY child.relname;
text
   partition    |                      bound
----------------+-------------------------------------------------
 orders_2026_01 | FOR VALUES FROM ('2026-01-01') TO ('2026-02-01')
(1 row)

详细说明 ​

date_trunc 对齐分区边界 ​

手工写 '2026-01-01' 容易在跨月、跨年时算错。用 date_trunc 生成边界更可靠——但要注意:它生成的是时间戳,作为边界常量需要能隐式转换为分区键类型。

sql
-- 用 date_trunc 对齐到月初,生成稳定的边界常量
SELECT date_trunc('month', DATE '2026-01-15')::date AS month_start,
       (date_trunc('month', DATE '2026-01-15') + interval '1 month')::date AS month_end;
text
 month_start | month_end
-------------+------------
 2026-01-01  | 2026-02-01
(1 row)

批量预建分区时,用它生成 DDL 比手写更安全:

sql
-- 生成未来 12 个月的建分区语句
SELECT format(
  'CREATE TABLE orders_%s PARTITION OF orders FOR VALUES FROM (%L) TO (%L);',
  to_char(m, 'YYYY_MM'), m::date, (m + interval '1 month')::date
) AS ddl
FROM generate_series(
       date_trunc('month', DATE '2026-01-01'),
       date_trunc('month', DATE '2026-12-01'),
       interval '1 month') AS m;
text
                                        ddl
------------------------------------------------------------------------------------
 CREATE TABLE orders_2026_01 PARTITION OF orders FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
 CREATE TABLE orders_2026_02 PARTITION OF orders FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
 ...
(12 rows)

为什么用 format 而不是字符串拼接

%L 会正确加引号与转义,%s 用于标识符片段。手工 || 拼接字符串在遇到特殊字符或时区时会出错。

边界必须是该类型的常量 ​

分区边界表达式在建表时就要能求值为常量,不能依赖运行期数据:

sql
-- 错误!partition bound is not a constant
CREATE TABLE bad_part PARTITION OF orders
  FOR VALUES FROM (CURRENT_DATE) TO (CURRENT_DATE + 1);
text
ERROR:  cannot use column reference in partition bound expression

正确做法是在应用层算好日期再拼 DDL,或者用 DO 块动态执行:

sql
DO $$
DECLARE
  m date := date_trunc('month', CURRENT_DATE)::date;
BEGIN
  EXECUTE format(
    'CREATE TABLE IF NOT EXISTS orders_%s PARTITION OF orders FOR VALUES FROM (%L) TO (%L)',
    to_char(m, 'YYYY_MM'), m, (m + interval '1 month')::date
  );
END $$;

时间戳与日期类型的边界陷阱 ​

如果分区键是 timestamptz,边界常量会按会话时区解释,跨时区部署时可能错位:

sql
-- 分区键为 timestamptz
CREATE TABLE events (id bigint, ts timestamptz) PARTITION BY RANGE (ts);

-- 边界 '2026-01-01' 会被当作本地时区的 2026-01-01 00:00
CREATE TABLE events_2026_01 PARTITION OF events
  FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

建议显式带时区偏移,避免服务器时区变化导致边界漂移:

sql
CREATE TABLE events_2026_01 PARTITION OF events
  FOR VALUES FROM ('2026-01-01 00:00:00+08') TO ('2026-02-01 00:00:00+08');

如果业务只关心「哪一天」,直接用 date 类型分区键更省心:

sql
created_at date NOT NULL   -- 推荐:边界语义无歧义

多列范围键与元组比较 ​

多列范围键按字典序比较,很适合「多租户 + 时间」的组合:

sql
CREATE TABLE metrics (
  tenant_id  int  NOT NULL,
  logdate    date NOT NULL,
  value      numeric
) PARTITION BY RANGE (tenant_id, logdate);
text
CREATE TABLE
sql
CREATE TABLE metrics_t1_2026_01 PARTITION OF metrics
  FOR VALUES FROM (1, '2026-01-01') TO (1, '2026-02-01');
CREATE TABLE metrics_t2_2026_01 PARTITION OF metrics
  FOR VALUES FROM (2, '2026-01-01') TO (2, '2026-02-01');
text
CREATE TABLE
CREATE TABLE

路由判断是元组比较:(tenant_id, logdate) >= (1, '2026-01-01') AND < (1, '2026-02-01')。

sql
INSERT INTO metrics VALUES (1, '2026-01-20', 42);
SELECT tableoid::regclass AS partition FROM metrics;
text
      partition
----------------------
 metrics_t1_2026_01
(1 row)

多列范围键的裁剪条件更严格

只有查询条件同时约束了前缀列,裁剪才有效。WHERE logdate = '2026-01-20' 单独出现时无法裁剪(因为第一列 tenant_id 未受限)——查询里必须带 tenant_id。

插入路由失败:no partition of relation ... found for row ​

这是范围分区最常见的运行期错误。它只在插入/更新时出现,含义是「分区键的值不落在任何分区的区间内」。

sql
INSERT INTO orders VALUES (99, 1, 1.00, '2027-06-01');
text
ERROR:  no partition of relation "orders" found for row
DETAIL:  Partition key of the failing row contains (created_at) = (2027-06-01).

排查顺序:

步骤命令目的
1SELECT pg_get_expr(relpartbound, oid) FROM pg_class WHERE relname='orders'确认已有边界
2检查报错 DETAIL 里的键值定位越界值
3确认是否漏建某个月的分区找区间空洞
4若有 DEFAULT 分区,检查其内容兜底分区可能已积压数据
5补建缺失分区或修正数据修复
sql
-- 一次列出所有分区的边界,检查有无空洞
SELECT
  child.relname AS partition,
  pg_get_expr(child.relpartbound, child.oid) AS bound
FROM pg_inherits i
JOIN pg_class child ON i.inhrelid = child.oid
JOIN pg_class parent ON i.inhparent = parent.oid
WHERE parent.relname = 'orders'
ORDER BY bound;

ORDER BY bound 会把边界按表达式文本排序(对本例的 ISO 日期串恰好等价于时间序),便于肉眼扫出空缺月份。

DEFAULT 分区:兜底与它的代价 ​

DEFAULT 分区接收所有「不匹配任何其他分区」的行:

sql
CREATE TABLE orders_default PARTITION OF orders DEFAULT;
text
CREATE TABLE
sql
-- 越界的 2027 年数据不再报错,而是落进 default
INSERT INTO orders VALUES (99, 1, 1.00, '2027-06-01');
SELECT tableoid::regclass AS partition FROM orders WHERE id = 99;
text
   partition
----------------
 orders_default
(1 row)

它的价值与风险都很明确:

价值风险
越界数据不丢,系统不中断数据「悄悄」进入不该去的地方
给预建分区留出容错窗口一旦 DEFAULT 里存在属于新分区的行,就无法创建该新分区
sql
-- 错误!updated partition constraint for default partition would be violated
CREATE TABLE orders_2027_06 PARTITION OF orders
  FOR VALUES FROM ('2027-06-01') TO ('2027-07-01');
text
ERROR:  updated partition constraint for default partition "orders_default" would be violated by some row

正确做法:先把越界行移出去,再加分区。

sql
-- 建临时表承接,再迁回
CREATE TABLE orders_tmp (LIKE orders INCLUDING ALL);
INSERT INTO orders_tmp SELECT * FROM orders_default WHERE created_at >= '2027-06-01';
DELETE FROM orders_default WHERE created_at >= '2027-06-01';

ALTER TABLE orders DETACH PARTITION orders_default;
CREATE TABLE orders_2027_06 PARTITION OF orders
  FOR VALUES FROM ('2027-06-01') TO ('2027-07-01');
ALTER TABLE orders ATTACH PARTITION orders_default DEFAULT;
INSERT INTO orders SELECT * FROM orders_tmp;

生成列与触发器辅助路由 ​

分区键必须是表中真实存在的列(或它的表达式)。如果原始数据只有 created_at 时间戳、却想按「业务日」分区,可以用生成列(Generated Column):

sql
CREATE TABLE events (
  id         bigint      NOT NULL,
  created_at timestamptz NOT NULL,
  biz_date   date        NOT NULL GENERATED ALWAYS AS ((created_at AT TIME ZONE 'Asia/Shanghai')::date) STORED
) PARTITION BY RANGE (biz_date);
text
CREATE TABLE

存储生成列可以参与分区键。注意两个约束:

约束说明
表达式必须 IMMUTABLEAT TIME ZONE 'Asia/Shanghai' 是 IMMUTABLE(字面量时区),可用
必须是 STORED(默认)虚拟生成列不能作为分区键

如果分区键需要依赖行内其他数据(如查表决定),生成列传不进去(不能引用其他表),此时只能在应用层写入正确的分区键列。

分区裁剪与范围分区的天然契合 ​

范围分区之所以最常用,是因为它天然支持 >=、<、BETWEEN 这类范围裁剪:

sql
EXPLAIN (COSTS OFF)
SELECT * FROM orders WHERE created_at >= '2026-01-01' AND created_at < '2026-02-01';
text
                 Append
                   Subplans Removed: 2
                   ->  Seq Scan on orders_2026_01
                         Filter: ((created_at >= '2026-01-01'::date) AND (created_at < '2026-02-01'::date))
(3 rows)

而下面这种写法会破坏裁剪,因为函数包住了分区键:

sql
-- 不推荐:date_trunc 包住分区键,无法在规划期裁剪
EXPLAIN (COSTS OFF)
SELECT * FROM orders WHERE date_trunc('month', created_at) = '2026-02-01';
text
               Append
                 ->  Seq Scan on orders_2026_01
                 ->  Seq Scan on orders_2026_02
                 ->  Seq Scan on orders_2026_03
(4 rows)

改成对分区键直接施加范围条件即可恢复裁剪:

sql
SELECT * FROM orders
WHERE created_at >= '2026-02-01' AND created_at < '2026-03-01';

轮转分区的标准流程 ​

范围分区最典型的运维模式是「每月新增一个、删除一个」:

sql
-- 1) 预建下个月的分区
CREATE TABLE orders_2026_04 PARTITION OF orders
  FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');

-- 2) 归档并删除最老的分区
ALTER TABLE orders DETACH PARTITION orders_2026_01;
-- (此时 orders_2026_01 已是普通表,可 COPY 备份或直接 DROP)
DROP TABLE orders_2026_01;
阶段锁级别备注
CREATE TABLE ... PARTITION OF父表 ACCESS EXCLUSIVE建议空闲窗口执行
ATTACH PARTITION父表 SHARE UPDATE EXCLUSIVE(PostgreSQL 12+)对并发更友好
DETACH PARTITION父表 ACCESS EXCLUSIVE快速
DETACH PARTITION CONCURRENTLY父表 SHARE UPDATE EXCLUSIVE(PostgreSQL 14+)不能在事务块内执行

实战示例 ​

场景:为 IoT 设备的读数表建立按天范围分区,处理越界数据,并验证裁剪。

第 1 步:建父表

sql
DROP TABLE IF EXISTS readings CASCADE;

CREATE TABLE readings (
  device_id  bigint        NOT NULL,
  ts         timestamptz   NOT NULL,
  value      numeric(10,3) NOT NULL
) PARTITION BY RANGE (ts);

第 2 步:预建三天的分区

sql
CREATE TABLE readings_2026_01_01 PARTITION OF readings
  FOR VALUES FROM ('2026-01-01 00:00:00+08') TO ('2026-01-02 00:00:00+08');
CREATE TABLE readings_2026_01_02 PARTITION OF readings
  FOR VALUES FROM ('2026-01-02 00:00:00+08') TO ('2026-01-03 00:00:00+08');
CREATE TABLE readings_2026_01_03 PARTITION OF readings
  FOR VALUES FROM ('2026-01-03 00:00:00+08') TO ('2026-01-04 00:00:00+08');

第 3 步:灌数

sql
INSERT INTO readings (device_id, ts, value)
SELECT
  (g % 50) + 1,
  TIMESTAMPTZ '2026-01-01 00:00:00+08' + (g || ' minutes')::interval,
  (random() * 100)::numeric(10,3)
FROM generate_series(1, 4320) AS g;   -- 3 天 × 1440 分钟
text
INSERT 0 4320

第 4 步:确认分布

sql
SELECT tableoid::regclass AS partition, count(*)
FROM readings GROUP BY 1 ORDER BY 1;
text
      partition       | count
----------------------+-------
 readings_2026_01_01  |  1440
 readings_2026_01_02  |  1440
 readings_2026_01_03  |  1440
(3 rows)

第 5 步:验证裁剪

sql
EXPLAIN (COSTS OFF)
SELECT count(*) FROM readings
WHERE ts >= '2026-01-02 00:00:00+08' AND ts < '2026-01-03 00:00:00+08';
text
           Aggregate
           ->  Append
                 Subplans Removed: 2
                 ->  Seq Scan on readings_2026_01_02
                       Filter: ((ts >= '2026-01-02 00:00:00+08'::timestamp with time zone) AND (ts < '2026-01-03 00:00:00+08'::timestamp with time zone))
(4 rows)

第 6 步:制造一次越界,观察报错

sql
INSERT INTO readings VALUES (7, '2026-02-10 10:00:00+08', 1.5);
text
ERROR:  no partition of relation "readings" found for row
DETAIL:  Partition key of the failing row contains (ts) = (2026-02-10 10:00:00+08).

第 7 步:加 DEFAULT 兜底后再插

sql
CREATE TABLE readings_default PARTITION OF readings DEFAULT;
INSERT INTO readings VALUES (7, '2026-02-10 10:00:00+08', 1.5);
SELECT tableoid::regclass AS partition FROM readings WHERE device_id = 7;
text
CREATE TABLE
INSERT 0 1
     partition
-------------------
 readings_default
(1 row)

第 8 步:清理

sql
DROP TABLE readings CASCADE;

注意事项 ​

  • 范围边界是左闭右开。 把上界写成「月底」而不是「下月初」会造成数据落不进任何分区。
sql
-- 错误!1 月 31 日的行无处可去
CREATE TABLE orders_2026_01 PARTITION OF orders
  FOR VALUES FROM ('2026-01-01') TO ('2026-01-31');
  • MINVALUE / MAXVALUE 是类型极值,不是 -inf 记号。 用了 TO (MAXVALUE) 之后就不能再建更大的分区。
sql
-- 不推荐:一旦这样写,未来只能靠 DEFAULT 或重建来承接新数据
CREATE TABLE logs_all PARTITION OF logs FOR VALUES FROM ('2026-01-01') TO (MAXVALUE);
  • 分区边界必须是常量。 不允许引用列、参数或易变函数。
sql
-- 错误!cannot use column reference in partition bound expression
CREATE TABLE p PARTITION OF orders FOR VALUES FROM (created_at) TO (created_at + 1);
  • timestamptz 分区键的边界受时区影响。 跨时区或多环境部署时务必显式写偏移量。
sql
-- 推荐:显式时区,避免依赖服务器 TimeZone 设置
FOR VALUES FROM ('2026-01-01 00:00:00+08') TO ('2026-02-01 00:00:00+08')
  • UPDATE 改分区键可能报「no partition ... found for row」。 这是搬行失败,不是数据问题本身。
sql
-- 目标月份没建分区时会失败
UPDATE orders SET created_at = '2027-06-01' WHERE id = 1;
  • DEFAULT 分区会阻塞新分区的创建。 务必定期清空它,或仅在短期使用。
sql
-- 错误!updated partition constraint for default partition would be violated
CREATE TABLE orders_2027_06 PARTITION OF orders
  FOR VALUES FROM ('2027-06-01') TO ('2027-07-01');
  • 函数包住分区键会破坏裁剪。 WHERE date_trunc('month', created_at) = ? 这类写法要改写成范围条件。
sql
-- 不推荐
WHERE date_trunc('day', created_at) = '2026-01-02'
-- 推荐
WHERE created_at >= '2026-01-02' AND created_at < '2026-01-03'
  • 多列范围键的裁剪要求前缀列被约束。 只约束第二列的查询无法裁剪。

  • 分区数不是越多越好。 按小时分区一年就是 8760 个分区,规划开销会明显上升。

  • ATTACH PARTITION 会扫描待挂表以校验边界约束。 若想跳过扫描,先在待挂表上建一个等价的 CHECK 约束(挂完后可删)。

sql
-- 预加 CHECK 可跳过 ATTACH 时的全表扫描
ALTER TABLE orders_staging ADD CONSTRAINT ck_bound
  CHECK (created_at >= DATE '2026-05-01' AND created_at < DATE '2026-06-01');
ALTER TABLE orders ATTACH PARTITION orders_staging
  FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
ALTER TABLE orders_staging DROP CONSTRAINT ck_bound;

最佳实践 ​

  • 时间表一律 RANGE 分区,粒度为月(高频表用天)。 粒度的选择标准是「单分区大小适中且轮转周期符合保留策略」。
sql
) PARTITION BY RANGE (created_at);
  • 用 format + generate_series 批量生成建分区 DDL,避免手工写边界出错。
sql
SELECT format('CREATE TABLE orders_%s PARTITION OF orders FOR VALUES FROM (%L) TO (%L);',
              to_char(m, 'YYYY_MM'), m::date, (m + interval '1 month')::date)
FROM generate_series(date_trunc('month', CURRENT_DATE),
                     date_trunc('month', CURRENT_DATE) + interval '11 months',
                     interval '1 month') AS m;
  • 提前预建未来 3~12 个月的分区,并设置监控告警。 「已使用到倒数第二个分区」就应告警。

  • DEFAULT 分区只作为短期兜底,并每日检查它是否为空。

sql
SELECT count(*) FROM orders_default;
  • 分区键类型优先选 date 而非 timestamptz,除非业务真的需要时区精度——date 的边界语义没有歧义。

  • ATTACH 大表前预加 CHECK 约束以跳过扫描,并在完成后删除该冗余约束。

sql
ALTER TABLE staging ADD CONSTRAINT ck_range CHECK (created_at >= DATE '2026-06-01' AND created_at < DATE '2026-07-01');
ALTER TABLE orders ATTACH PARTITION staging FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
  • 把轮转脚本纳入定时任务,并在切换时校验新分区已成功创建。
sql
SELECT count(*) > 0 AS ok FROM pg_class c
JOIN pg_inherits i ON i.inhrelid = c.oid
JOIN pg_class p ON p.oid = i.inhparent
WHERE p.relname = 'orders' AND c.relname = 'orders_' || to_char(CURRENT_DATE, 'YYYY_MM');
  • 查询里始终用「分区键的范围条件」表达时间过滤,让裁剪生效。
sql
WHERE created_at >= '2026-01-01' AND created_at < '2026-02-01'
  • 删除历史数据用 DETACH + DROP,而不是 DELETE。
sql
ALTER TABLE orders DETACH PARTITION orders_2026_01;
DROP TABLE orders_2026_01;

下一节

继续学习:列表分区(LIST)

参考链接 ​