ORA-14400 inserted partition key does not map to any partition错误
这几天每天都在通宵,白天休息时间很少,身体有些吃不消,还好总监又带了两个DBA过来,白天终于可以好好休息了,正在酒店睡觉,被DBA的电话叫醒,说是遇到了分区不存在导致数据无法插入的错误,那张表是按照时间字段的RANGE分区,使用11g的新特性,自动分区(INTERVAL),一天一个分区,怎么会遇到找不到分区的错误呢?出问题的数据是2015年7月21号的数据,最近也是通宵比较多,一时没反应过来,第一反应是不是闰秒的问题导致的,可是在去现场的路上想了下,7月21号早就过了闰秒的日期了。
到了现场,其他DBA手动添加了分区,解决了数据插不进去的问题,我和DBA说那个分区表是自动扩展分区的,不需要手动维护。怎么会出现这个问题,问他看没看告警日志,然后他一拍大腿,说那张表他DROP后重建的,重建的时候没有指定自动扩展分区,他是使用PLSQL Developer工具右键查看表,查看SQL,使用这个SQL创建的,这个表当时的分区停留在7月21号之前,所以他创建的表只有7月21号之前的分区,7月21号及之后的数据就找不到对应的分区了。
以下模拟下这个过程,先创建分区表ORD_PAY_PART。
SQL> CREATE TABLE "CHGSHS"."ORD_PAY_PART" 2 ( "ORD_ID" VARCHAR2(40) NOT NULL ENABLE, 3 "PAY_SEQ" VARCHAR2(5) NOT NULL ENABLE, 4 "PAY_MNS_CD" VARCHAR2(7) NOT NULL ENABLE, 5 "PAY_ORD_PTR_CD" VARCHAR2(7) NOT NULL ENABLE, 6 "PAY_ARR_AMT" NUMBER(21,2) NOT NULL ENABLE, 7 "PAY_AMT" NUMBER(21,2), 8 "PAY_DTM" DATE, 9 "PAY_MNS_RLTD_ID" VARCHAR2(40), 10 "RFN_AMT" NUMBER(21,2) DEFAULT 0, 11 "RFN_PSS_AMT" NUMBER(21,2) DEFAULT 0, 12 "ORG_ORD_ID" VARCHAR2(40) NOT NULL ENABLE, 13 "ORG_PAY_SEQ" VARCHAR2(5) NOT NULL ENABLE, 14 "ORG_MNS_CD" VARCHAR2(7) NOT NULL ENABLE, 15 "INST_ID" VARCHAR2(40) NOT NULL ENABLE, 16 "INST_DTM" DATE DEFAULT SYSDATE NOT NULL ENABLE, 17 "MDF_ID" VARCHAR2(40) NOT NULL ENABLE, 18 "MDF_DTM" DATE DEFAULT SYSDATE NOT NULL ENABLE, 19 CONSTRAINT "PK_ORD_PAY_1" PRIMARY KEY ("ORD_ID", "PAY_MNS_CD") 20 USING INDEX 21 TABLESPACE "TS_INX_ORD" ENABLE 22 ) 23 TABLESPACE "TS_ORD" 24 PARTITION BY RANGE ("MDF_DTM") INTERVAL (NUMTODSINTERVAL(1, 'DAY')) 25 (PARTITION "P1" VALUES LESS THAN 26 (TO_DATE(' 2013-08-25 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) 27 SEGMENT CREATION IMMEDIATE 28 TABLESPACE "TS_ORD" ) 29 ; Table created.
插入7月1号之前的数据。
SQL> insert /* +append */ into "CHGSHS"."ORD_PAY_PART" select * from "CHGSHS"."ORD_PAY" where MDF_DTM < to_date('2015-07-01 00:00:00','yyyy-mm-dd hh24:mi:ss'); 14414226 rows created. SQL> commit; Commit complete.
此时,分区停留在7月1日之前,7月1日之后的分区不存在。使用PLSQL Developer工具查看表并查看的SQL,而不是METADABA DDL操作。这样看到的SQL并不是自带扩展分区的,而是列出了所有的分区。根据查看到的SQL创建分区表。
SQL> drop table chgshs.ORD_PAY_PART purge; Table dropped. SQL> create table CHGSHS.ORD_PAY_PART 2 ( 3 ord_id VARCHAR2(40) not null, 4 pay_seq VARCHAR2(5) not null, 5 pay_mns_cd VARCHAR2(7) not null, 6 pay_ord_ptr_cd VARCHAR2(7) not null, 7 pay_arr_amt NUMBER(21,2) not null, 8 pay_amt NUMBER(21,2), 9 pay_dtm DATE, 10 pay_mns_rltd_id VARCHAR2(40), 11 rfn_amt NUMBER(21,2) default 0, 12 rfn_pss_amt NUMBER(21,2) default 0, 13 org_ord_id VARCHAR2(40) not null, 14 org_pay_seq VARCHAR2(5) not null, 15 org_mns_cd VARCHAR2(7) not null, 16 inst_id VARCHAR2(40) not null, 17 inst_dtm DATE default SYSDATE not null, 18 mdf_id VARCHAR2(40) not null, 19 mdf_dtm DATE default SYSDATE not null 20 ) 21 partition by range (MDF_DTM) 22 ( 23 partition P1 values less than (TO_DATE(' 2013-08-25 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) 24 tablespace TS_ORD 25 pctfree 10 26 initrans 1 27 maxtrans 255 28 storage 29 ( 30 initial 8M 31 next 1M 32 minextents 1 33 maxextents unlimited 34 ), 35 partition SYS_P4319 values less than (TO_DATE(' 2013-08-26 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) 36 tablespace TS_ORD 37 pctfree 10 38 initrans 1 39 maxtrans 255 40 storage 41 ( 42 initial 8M 43 next 1M 44 minextents 1 45 maxextents unlimited 46 ), --由于分区太多,建表的SQL8千多行,此处省略大部分分区相关的代码 8123 partition SYS_P4978 values less than (TO_DATE(' 2015-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) 8124 tablespace TS_ORD 8125 pctfree 10 8126 initrans 1 8127 maxtrans 255 8128 storage 8129 ( 8130 initial 8M 8131 next 1M 8132 minextents 1 8133 maxextents unlimited 8134 ) 8135 ); Table created. SQL> alter table CHGSHS.ORD_PAY_PART 2 add constraint PK_ORD_PAY_1 primary key (ORD_ID, PAY_MNS_CD) 3 using index 4 tablespace TS_INX_ORD 5 pctfree 10 6 initrans 2 7 maxtrans 255 8 storage 9 ( 10 initial 64K 11 next 1M 12 minextents 1 13 maxextents unlimited 14 ); Table altered.
从建表的SQL可以看到,此时的表分区停留在7月1号之前,而且没有DEFAULT分区,7月1日之后的数据将插入不进去。
SQL> insert /* +append */ into "CHGSHS"."ORD_PAY_PART" select /* +parallel 8 */ * from "CHGSHS"."ORD_PAY" where MDF_DTM >to_date('2015-07-01 00:00:00','yyyy-mm-dd hh24:mi:ss'); insert /* +append */ into "CHGSHS"."ORD_PAY_PART" select /* +parallel 8 */ * from "CHGSHS"."ORD_PAY" where MDF_DTM >to_date('2015-07-01 00:00:00','yyyy-mm-dd hh24:mi:ss') * ERROR at line 1: ORA-14400: inserted partition key does not map to any partition
手动添加分区之后,可以插入数据。
SQL> alter table CHGSHS.ORD_PAY_PART ADD partition P2 values less than (TO_DATE(' 2015-07-02 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace TS_ORD; Table altered. SQL> insert /* +append */ into "CHGSHS"."ORD_PAY_PART" select /* +parallel 8 */ * from "CHGSHS"."ORD_PAY" where MDF_DTM >to_date('2015-07-01 00:00:00','yyyy-mm-dd hh24:mi:ss') AND MDF_DTM <to_date('2015-07-02 00:00:00','yyyy-mm-dd hh24:mi:ss'); 26120 rows created. SQL> COMMIT; Commit complete.
11g可以将分区表修改为字段扩展分区,就不需要手动添加分区了。
SQL> alter table CHGSHS.ORD_PAY_PART set INTERVAL (NUMTODSINTERVAL(1, 'DAY')); Table altered. SQL> SQL> SQL> insert /* +append */ into "CHGSHS"."ORD_PAY_PART" select /* +parallel 8 */ * from "CHGSHS"."ORD_PAY" where MDF_DTM >to_date('2015-07-02 00:00:00','yyyy-mm-dd hh24:mi:ss'); 501418 rows created. SQL> commit; Commit complete.
如果数据库打开了enable_ddl_logging参数,在告警日志就可以看到扩展分区的信息。
SQL> show parameter enable_ddl_logging NAME TYPE VALUE ------------------------------------ ---------------------- ------------------------------ enable_ddl_logging boolean TRUE Wed Jul 29 22:14:46 2015 alter table CHGSHS.ORD_PAY_PART set INTERVAL (NUMTODSINTERVAL(1, 'DAY')) Wed Jul 29 22:15:07 2015 TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4986 (682) VALUES LESS THAN (TO_DATE(' 2015-07-08 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4987 (680) VALUES LESS THAN (TO_DATE(' 2015-07-06 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4988 (677) VALUES LESS THAN (TO_DATE(' 2015-07-03 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4989 (678) VALUES LESS THAN (TO_DATE(' 2015-07-04 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4990 (697) VALUES LESS THAN (TO_DATE(' 2015-07-23 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4991 (683) VALUES LESS THAN (TO_DATE(' 2015-07-09 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4992 (686) VALUES LESS THAN (TO_DATE(' 2015-07-12 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4993 (681) VALUES LESS THAN (TO_DATE(' 2015-07-07 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4994 (685) VALUES LESS THAN (TO_DATE(' 2015-07-11 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4995 (684) VALUES LESS THAN (TO_DATE(' 2015-07-10 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4996 (679) VALUES LESS THAN (TO_DATE(' 2015-07-05 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4997 (698) VALUES LESS THAN (TO_DATE(' 2015-07-24 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4998 (691) VALUES LESS THAN (TO_DATE(' 2015-07-17 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P4999 (689) VALUES LESS THAN (TO_DATE(' 2015-07-15 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5000 (692) VALUES LESS THAN (TO_DATE(' 2015-07-18 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5001 (690) VALUES LESS THAN (TO_DATE(' 2015-07-16 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5002 (693) VALUES LESS THAN (TO_DATE(' 2015-07-19 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5003 (687) VALUES LESS THAN (TO_DATE(' 2015-07-13 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5004 (688) VALUES LESS THAN (TO_DATE(' 2015-07-14 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5005 (699) VALUES LESS THAN (TO_DATE(' 2015-07-25 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5006 (702) VALUES LESS THAN (TO_DATE(' 2015-07-28 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) TABLE CHGSHS.ORD_PAY_PART: ADDED INTERVAL PARTITION SYS_P5007 (703) VALUES LESS THAN (TO_DATE(' 2015-07-29 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
11g的这个字段扩展分区还是很方便的,10g的时候通常要写存储过程来扩展分区,11g自己就可以做这个事情。