Oracle 11g起支持在虚拟列上直接分区,本质是将计算逻辑“下沉”到分区键层,无需真实存储、不占空间但可参与分区裁剪;虚拟列须为GENERATED ALWAYS AS类型且表达式仅引用本表已有列,创建时需在PARTITION BY子句中引用虚拟列名而非表达式,支持LIST/RANGE/HASH及组合分区,子分区键也可为虚拟列,但主分区不可用INTERVAL与虚拟列组合,且必须启用ENABLE ROW MOVEMENT以支持行迁移。
oracle 11g 起支持在虚拟列上直接分区,本质是把计算逻辑“下沉”到分区键层,不用真实存储、不占空间,但能参与分区裁剪。关键点在于:虚拟列必须是 generated always as 类型,且表达式里只能引用本表的已有列。
核心是把虚拟列名写进 PARTITION BY 子句,而不是原始列。常见错误是误把表达式直接塞进去,比如写成 PARTITION BY LIST (SUBSTR(c1,1,1)) —— 这会报错,必须先定义虚拟列再引用它。
SUBSTR(c1,1,1) 自动为 VARCHAR2(1))LIST、RANGE、HASH,也支持组合分区(如 RANGE 主分区 + LIST 子分区)total_amount AS (quantity_sold * amount_sold) 做子分区键ENABLE ROW MOVEMENT 必须开启,否则当虚拟列值变化导致行需跨分区时会报 ORA-14402
适合按编码规则分类,比如从 bureau_code 提取前两位作为省份代码。这种场景下,虚拟列让分区逻辑和业务规则解耦,后续改规则只需改虚拟列表达式,不用动分区结构。
VALUES,不能用 DEFAULT 以外的通配(否则插入未覆盖值会报 ORA-14400)INSERT INTO test(bureau_code) VALUES('0101') 即可,千万别写 INSERT ... (bureau_code, province_code)
SELECT * FROM test PARTITION(p1) 直接定位分区,执行计划里能看到 Pstart=Pstop=1,证明走了分区裁剪用虚拟列做周/月/年提取(如 TO_NUMBER(TO_CHAR(getdate, 'D')))看似方便,但要注意 NLS 设置影响结果。比如 'D' 返回的是本周第几天,但第一天是周日还是周一,取决于 NLS_TERRITORY。
ALTER SESSION SET NLS_TERRITORY='AMERICA' 或明确指定 NLS_DATE_LANGUAGE,否则不同环境行为不一致SYS_CONTEXT),Oracle 不允许这类非确定性表达式INTERVAL 分区不能和虚拟列组合用于主分区键(只支持真实列),但可用于子分区——例如主键用 time_id 做 INTERVAL,子分区键用虚拟列真正麻烦的是跨分区更新:一旦某行因虚拟列值变更需要迁移,ENABLE ROW MOVEMENT 开了也不代表万事大吉——如果该行上有全局索引,迁移会触发索引维护开销;若用了物化视图或 CDC 工具,还得确认它们是否识别虚拟列变动。这些隐含成本,比建表时多敲几行 SQL 重要得多。