Oracle 分區表的 FOR 語句,你這樣用過嗎?

原文鏈接:https://www.modb.pro/db/15418


在11g以後,Oracle簡化了指定分區的方式,不再需要明確指定分區名稱,而是可以通過指定分區鍵值列數據的方式來指向對應的分區。

指定一個分區除了使用分區名稱外,很多時候還可以使用FOR語句。
從11g開始,對分區進行操作的時候,不僅可以使用分區名稱,還可以使用FOR語句。


在10g中,MERGE RANGE分區的語句如下:

<code>SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
PL/SQL Release 10.2.0.3.0 - Production
CORE    10.2.0.3.0      Production
TNS for Solaris: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production
SQL> CREATE TABLE T_PART_RANGE
  2  (ID NUMBER, 
  3  NAME VARCHAR2(30), 
  4  CREATE_DATE DATE)
  5  PARTITION BY RANGE (CREATE_DATE) 
  6  (PARTITION P1 VALUES LESS THAN (TO_DATE('2009-1', 'YYYY-MM')),
  7  PARTITION P2 VALUES LESS THAN (TO_DATE('2009-4', 'YYYY-MM')),
  8  PARTITION P3 VALUES LESS THAN (TO_DATE('2009-7', 'YYYY-MM')));表已創建。SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
SQL> ALTER TABLE T_PART_RANGE 
  2  MERGE PARTITIONS P2, P3
  3  INTO PARTITION P3;表已更改。/<code>


而在11g中,除了使用分區名稱外,還可以使用FOR語句來代替,比如:

<code>SQL> SELECT * FROM V$VERSION;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL> CREATE TABLE T_PART_RANGE
  2  (ID NUMBER, 
  3  NAME VARCHAR2(30), 
  4  CREATE_DATE DATE)
  5  PARTITION BY RANGE (CREATE_DATE) 
  6  (PARTITION P1 VALUES LESS THAN (TO_DATE('2009-1', 'YYYY-MM')),
  7  PARTITION P2 VALUES LESS THAN (TO_DATE('2009-4', 'YYYY-MM')),
  8  PARTITION P3 VALUES LESS THAN (TO_DATE('2009-7', 'YYYY-MM')));表已創建。SQL> ALTER TABLE T_PART_RANGE
  2  MERGE PARTITIONS
  3  FOR(TO_DATE('2009-01', 'YYYY-MM')), 
  4  FOR(TO_DATE('2009-04', 'YYYY-MM'))
  5  INTO PARTITION P3;表已更改。/<code>


這種語法的優勢對於範圍分區還不是很明顯,而對於INTERVAL分區就十分有意義了。由於INTERVAL分區的分區名稱是系統產生的,用戶對INTERVAL分區最直觀的莫過於存在分區中的數據的範圍,根據分區的定義和INTERVAL的設置很容易可以確定分區的範圍和其中的數據,但是分區的名稱就必須通過數據字典才能查詢得到。

一個INTERVAL分區的簡單的例子:

<code>SQL> CREATE TABLE T_PART_INTER
  2  (ID NUMBER, 
  3  NAME VARCHAR2(30), 
  4  CREATE_DATE DATE)
  5  PARTITION BY RANGE (CREATE_DATE) 
  6  INTERVAL (INTERVAL '3' MONTH)
  7  (PARTITION P1 VALUES LESS THAN (TO_DATE('2009-1', 'YYYY-MM')),

  8  PARTITION P2 VALUES LESS THAN (TO_DATE('2009-4', 'YYYY-MM')));表已創建。SQL> INSERT INTO T_PART_INTER
  2  SELECT ROWNUM, OBJECT_NAME, SYSDATE - ROWNUM * 10
  3  FROM USER_OBJECTS;已創建9行。SQL> COMMIT;提交完成。SQL> ALTER TABLE T_PART_INTER
  2  MERGE PARTITIONS
  3  FOR(TO_DATE('2009-10', 'YYYY-MM')),
  4  FOR(TO_DATE('2010-1', 'YYYY-MM'));表已更改。/<code>


繼續上面的例子:

<code>SQL> CREATE TABLE T_PART_RANGE
  2  (ID NUMBER,
  3  NAME VARCHAR2(30),
  4  CREATE_DATE DATE)
  5  PARTITION BY RANGE (CREATE_DATE)
  6  (PARTITION P1 VALUES LESS THAN (TO_DATE('2009-1', 'YYYY-MM')),
  7  PARTITION P2 VALUES LESS THAN (TO_DATE('2009-4', 'YYYY-MM')),
  8  PARTITION P3 VALUES LESS THAN (TO_DATE('2009-7', 'YYYY-MM')));表已創建。/<code>


下面打算通過FOR語句的方式合併P2和P3分區:

<code>SQL> ALTER TABLE T_PART_RANGE
  2  MERGE PARTITIONS
  3  FOR(TO_DATE('2009-4', 'YYYY-MM')),
  4  FOR(TO_DATE('2009-7', 'YYYY-MM'))
  5  INTO PARTITION P3;
ALTER TABLE T_PART_RANGE
            *第 1 行出現錯誤:ORA-14702: 分區編號無效或超出範圍
語句出現了ORA-14702錯誤,查詢Oracle的錯誤文檔:
ORA-14702: The partition number is invalid or out-of-range
Cause: Attempted to use nonnumerical value or the number was out of range of the partitions.
Action: Use a valid partition number./<code>


根據錯誤文檔的描述,感覺是分區鍵值指定出現了錯誤,查詢分區信息:

<code>SQL> SELECT PARTITION_NAME, HIGH_VALUE
  2  FROM USER_TAB_PARTITIONS
  3  WHERE TABLE_NAME = 'T_PART_RANGE'
  4  ORDER BY 1;
PARTITION_NAME HIGH_VALUE
-------------- ----------------------------------------------------------------------------------
P1             TO_DATE(' 2009-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
P2             TO_DATE(' 2009-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
P3             TO_DATE(' 2009-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')/<code>


難道是分區鍵值指定有問題:

<code>SQL> ALTER TABLE T_PART_RANGE
  2  MERGE PARTITIONS
  3  FOR(TO_DATE(' 2009-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  4  FOR(TO_DATE(' 2009-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
  5  INTO PARTITION P3;
ALTER TABLE T_PART_RANGE
            *
第 1 行出現錯誤:ORA-14702: 分區編號無效或超出範圍/<code>


最終發現了問題所在,FOR語句中指定的並不是分區定義時使用的值,而是存儲在當前分區中的值:

<code>SQL> ALTER TABLE T_PART_RANGE
  2  MERGE PARTITIONS
  3  FOR(TO_DATE('2009-1', 'YYYY-MM')),
  4  FOR(TO_DATE('2009-4', 'YYYY-MM'));表已更改。SQL> SELECT PARTITION_NAME, HIGH_VALUE
  2  FROM USER_TAB_PARTITIONS
  3  WHERE TABLE_NAME = 'T_PART_RANGE'
  4  ORDER BY 1;
PARTITION_NAME  HIGH_VALUE
--------------- ---------------------------------------------------------------------------------
P1              TO_DATE(' 2009-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
SYS_P78         TO_DATE(' 2009-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')可以看到,在使用FOR語句的時候,是根據P1和P2分區定義時的日期指定的分區,但是MERGE的結果卻是P2和P3分區進行了合併。/<code>


因此Oracle並非根據分區定義來判斷分區,而是根據用戶給出的值,來判斷所屬分區,所以,P1分區和SYS_P78分區的合併完全可以寫成:

<code>SQL> ALTER TABLE T_PART_RANGE
  2  MERGE PARTITIONS
  3  FOR(TO_DATE('1970-1', 'YYYY-MM')),
  4  FOR(TO_DATE('2009-5', 'YYYY-MM'));表已更改。/<code>


由於FOR語句的這種特性,使得HASH分區也可以使用這個特性:

<code>SQL> CREATE TABLE T_PART_HASH
  2  (ID NUMBER,
  3  NAME VARCHAR2(30))
  4  PARTITION BY HASH(ID)
  5  PARTITIONS 16;表已創建。SQL> ALTER TABLE T_PART_HASH
  2  MOVE PARTITION FOR(6);表已更改。/<code>


這個例子對包含ID為6的分區進行了MOVE操作,而且甚至不需要指定的ID存在。

最後給一個簡單的LIST分區的SPLIT的例子:

<code>SQL> CREATE TABLE T_PART_LIST
  2  (
  3     OWNER VARCHAR2(30),
  4     TABLE_NAME VARCHAR2(30),
  5     TABLESPACE_NAME VARCHAR2(30),
  6     STATUS VARCHAR2(18)
  7  )

  8  PARTITION BY LIST (TABLESPACE_NAME)
  9  (
 10  PARTITION P1 VALUES ('SYSTEM'),
 11  PARTITION P2 VALUES ('YANGTK'),
 12  PARTITION P3 VALUES (DEFAULT)
 13  );表已創建。SQL> ALTER TABLE T_PART_LIST
  2  SPLIT PARTITION FOR('SYSAUX')
  3  VALUES ('SYSAUX')
  4  INTO (PARTITION P3, PARTITION P4);表已更改。SQL> SELECT PARTITION_NAME, HIGH_VALUE
  2  FROM USER_TAB_PARTITIONS
  3  WHERE TABLE_NAME = 'T_PART_LIST'
  4  ORDER BY 1;
PARTITION_NAME  HIGH_VALUE
--------------- -------------------------------------
P1              'SYSTEM'
P2              'YANGTK'
P3              'SYSAUX'
P4              DEFAULT/<code>


想了解更多關於數據庫、雲技術的內容嗎?

快來關注“數據和雲"、"雲和恩墨,"公眾號及"雲和恩墨"官方網站,我們期待大家一同學習與進步!

Oracle 分區表的 FOR 語句,你這樣用過嗎?

小程序”DBASK“在線問答,隨時解惑,歡迎瞭解和關注!

Oracle 分區表的 FOR 語句,你這樣用過嗎?


分享到:


相關文章: