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にペルシア暦を使用させる方法はありますか?
データベースレベルの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
を無視する必要があります。しかし、おそらくあなたは少なくとも時々ミックスを必要とするでしょう。
サラム! 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;