web-dev-qa-db-ja.com

ペルシア暦を使用した間隔によるOracleパーティション

OracleはDDLクエリでペルシア語(Jalali)カレンダーをサポートしています。

select to_char(register_date, 'YYYY-MM-DD', 'nls_calendar=persian')
  from my_table;

私はテーブルを次のように作成しました:

create table test_temp_times (
  id number(18) not null,
  xdate date not null,
  str varchar2(20))
partition by range(xdate)
interval(NUMTOYMINTERVAL(1, 'MONTH'))
(partition p0 values less than (to_date('13920101', 'YYYYMMDD', 'nls_calendar=persian')))
enable row movement;

テーブルは通常どおり作成されますが、レコードを追加し、Oracleが新しいパーティションを作成すると、パーティションは次のようになります。

create table TEMP_TIMES (
  id    NUMBER(18) not null,
  xdate DATE not null,
  str   VARCHAR2(20)
)
partition by range (XDATE)
(
  partition P0 values less than (TO_DATE(' 2013-03-21 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  partition SYS_P61 values less than (TO_DATE(' 2013-04-21 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  partition SYS_P62 values less than (TO_DATE(' 2013-05-21 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  partition SYS_P63 values less than (TO_DATE(' 2013-06-21 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  partition SYS_P64 values less than (TO_DATE(' 2013-07-21 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')));

ご覧のとおり、データベースはNLS_CALENDARをgregorian(13920101と同じ日)に変更し、各パーティションはgregorianカレンダーではなくpersianカレンダーに従って作成されます。

新しいパーティションを作成するためにOracleにペルシア暦を使用させる方法はありますか?

5
Amir Pashazadeh

データベースレベルのNLS_CALENDARとは異なるカレンダーで間隔を定義する方法がありません。仮想列を使用して、各日付が該当する(ペルシャ語)月の数値表現でパーティション化することにより、同じ効果を得ることができます。

create table test_temp_times (
  id number(18) not null,
  xdate date not null,
  str varchar2(20),
  ydate as (to_number(to_char(xdate, 'YYYYMM', 'nls_calendar=persian')))
)
partition by range(ydate)
interval(1)
(partition p0 values less than (139201))
enable row movement;

これに、サンプルの開始日の翌年の毎日のレコードが入力されている場合:

insert into test_temp_times (id, xdate, str)
select level, date '2013-03-20' + level, null
from dual
connect by level < 366;

作成されるパーティションは次のようになります。

select table_name, partition_name, high_value
from user_tab_partitions where table_name = 'TEST_TEMP_TIMES';

TABLE_NAME                     PARTITION_NAME                 HIGH_VALUE
------------------------------ ------------------------------ ----------
TEST_TEMP_TIMES                P0                             139201     
TEST_TEMP_TIMES                SYS_P479                       139202     
TEST_TEMP_TIMES                SYS_P480                       139203     
TEST_TEMP_TIMES                SYS_P481                       139204     
TEST_TEMP_TIMES                SYS_P482                       139205     
TEST_TEMP_TIMES                SYS_P483                       139206     
TEST_TEMP_TIMES                SYS_P484                       139207     
TEST_TEMP_TIMES                SYS_P485                       139208     
TEST_TEMP_TIMES                SYS_P486                       139209     
TEST_TEMP_TIMES                SYS_P487                       139210     
TEST_TEMP_TIMES                SYS_P488                       139211     
TEST_TEMP_TIMES                SYS_P489                       139212     
TEST_TEMP_TIMES                SYS_P490                       139213     

 13 rows selected 

また、月の境界がどのパーティションに該当するかを確認できます。

select utp.partition_name, min(ttt.xdate), max(ttt.xdate)
from test_temp_times ttt
join user_objects uo on uo.object_id = dbms_rowid.rowid_object(ttt.rowid)
join user_tab_partitions utp on utp.table_name = uo.object_name
and utp.partition_name = uo.subobject_name
group by utp.partition_name
order by partition_name;

PARTITION_NAME                 MIN(TTT.XDATE) MAX(TTT.XDATE)
------------------------------ -------------- --------------
P0                             2013-03-20     2013-03-20     
SYS_P479                       2013-03-21     2013-04-20     
SYS_P480                       2013-04-21     2013-05-21     
SYS_P481                       2013-05-22     2013-06-21     
SYS_P482                       2013-06-22     2013-07-22     
SYS_P483                       2013-07-23     2013-08-22     
SYS_P484                       2013-08-23     2013-09-22     
SYS_P485                       2013-09-23     2013-10-22     
SYS_P486                       2013-10-23     2013-11-21     
SYS_P487                       2013-11-22     2013-12-21     
SYS_P488                       2013-12-22     2014-01-20     
SYS_P489                       2014-01-21     2014-02-19     
SYS_P490                       2014-02-20     2014-03-19

少なくとも、それはあなたが達成しようとしていることだと思います...残念ながら、SQL Fiddleにはパーティション分割オプションがないため、デモを追加することはできませんが、これは11.2に対してテストされています。 .0.3。

もちろん、クエリのパーティションを使用するようにする必要があります...

select * from test_temp_times
where xdate = date '2013-11-01';

計画のある行を見つけます。


-----------
| Id  | Operation           | Name            | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                 |     1 |    47 |   164   (0)| 00:00:02 |       |       |
|   1 |  PARTITION RANGE ALL|                 |     1 |    47 |   164   (0)| 00:00:02 |     1 |1048575|
|*  2 |   TABLE ACCESS FULL | TEST_TEMP_TIMES |     1 |    47 |   164   (0)| 00:00:02 |     1 |1048575|
-------------------------------------------------------------------------------------------------------

仮想列をクエリに明示的に追加した場合:

select * from test_temp_times
where xdate = date '2013-11-01'
and ydate = to_number(to_char(date '2013-11-01', 'YYYYMM', 'nls_calendar=persian'));

次に、照会するパーティションを認識します。

----------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name            | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                 |     1 |    47 |    14   (0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE SINGLE|                 |     1 |    47 |    14   (0)| 00:00:01 |     9 |     9 |
|*  2 |   TABLE ACCESS FULL    | TEST_TEMP_TIMES |     1 |    47 |    14   (0)| 00:00:01 |     9 |     9 |
----------------------------------------------------------------------------------------------------------

明らかに、まだインデックスを作成していません。 1か月分のデータを探している場合は、単一のydate値に対してのみクエリを実行し、xdateを無視する必要があります。しかし、おそらくあなたは少なくとも時々ミックスを必要とするでしょう。

5
Alex Poole

サラム! Oracleに30日の間隔を使用するように言ったので、その間隔をsysdateに追加して使用します! numtodsinterval(1、 'MONTH')の代わりに、numtodsinterval(month_days(column_name)、 'DAY')を使用する方が良いと思います。month_daysは、次のようにペルシャの月の日数を計算します。

FUNCTION MONTH_DAYS (P_DATE IN DATE)
    RETURN NUMBER
IS
    V_MONTH_ID   NUMBER;
BEGIN
    V_MONTH_ID := TO_NUMBER (TO_CHAR (P_DATE, 'MM', 'NLS_CALENDAR=PERSIAN'));

    CASE V_MONTH_ID
        WHEN 1
        THEN
            RETURN 31;
            ...
    END CASE;
END;
1
hmmftg