次のような3つの列がある大きなテーブルがあります。
"START_DATE" DATE,
"START_VALUE" NUMBER(10,7)
"START_DATE_VALUE" NUMBER(18,7)
GENERATED ALWAYS AS
(
(extract(YEAR FROM START_DATE) * 10000 +
extract(MONTH FROM START_DATE)*100 +
extract(DAY FROM START_DATE))*power(10,3) +
(START_VALUE+180)
) VIRTUAL
START_DATE_VALUE
columnは、パーティション化に使用される仮想列です。ただし、次のようなクエリがある場合:
select *
from mytable
where
start_date > to_date('02-01-2012', 'MM-DD-YYYY')
and start_value > 120.23452
結果についてすべてのパーティションをスキャンします。 Oracleに仮想列を使用させ、適切なパーティションだけを選択して作業するにはどうすればよいですか?
テーブルの定義が非常に大きいため、ここにコピーすることはできません。
仮想列の定義に問題があると思います。あなたの特別な値について2012-02-01(inYYYY-MM-DD形式)および120.23452仮想列の値は
2012*10000+2*100+1*1000+180+120.23452 = 20120000+200+1000+300.23452 =20121500.23452
ではなく
20120201300.23452
あなたが期待したように。
また、仮想列がテーブルのパーティション化に使用されている列かどうかも確認してください。
VLDB and Partitioning Guide から:
仮想列ベースのパーティション分割テーブルは、SQLステートメントで仮想列を定義する式を使用するステートメントのパーティションプルーニングの恩恵を受けます。
select-statementだと思います
select * from mytable where start_date > todate('02-01-2012', 'MM-DD-YYYY') and start_value > 120.23452
のようなものでなければなりません
select * from mytable where (
extract(YEAR FROM START_DATE) * 10000 +
extract(MONTH FROM START_DATE)*100 +
extract(DAY FROM START_DATE))*power(10,3) +
(START_VALUE+180)
) > 20121500.23452
パーティション・プルーニングが行われるようにします。
の
todate
selectステートメントで使用する関数がOracle SQLに存在しません。関数の名前は
to_date
。
「Oracleで仮想列を使用するようにして、適切なパーティションを選択して作業する方法を教えてください。」
あなた(START_DATE, START_VALUE)
とSTART_DATE_VALUE
の間に関係があることを知っていますが、残念ながらオプティマイザはそうではありません。あなたが知っているのは、あなたがあなたのパーティショニング戦略で機能しない2つのカラムをクエリしているということだけです。
パーティションキーを投稿していないため、このテストケースで何をしているのかを推測しました。
create table t23
(id number not null primary key
, start_date date not null
, start_value number not null
, start_date_value number GENERATED ALWAYS AS
(
(extract(YEAR FROM START_DATE) * 10000 +
extract(MONTH FROM START_DATE)*100 +
extract(DAY FROM START_DATE))*power(10,3) +
(START_VALUE+180)
) VIRTUAL
)
PARTITION BY range (start_date_value)
(
PARTITION range_10 values LESS THAN (1900000000) ,
PARTITION range_20 values LESS THAN (2000000000),
PARTITION range_30 values LESS THAN (2100000000),
PARTITION range_40 values LESS THAN (2200000000),
PARTITION range_50 values LESS THAN (11000000000),
PARTITION range_60 values LESS THAN (12000000000),
PARTITION range_70 values LESS THAN (13000000000),
PARTITION range_80 values LESS THAN (to_date('01-JAN-1400')),
PARTITION range_90 values LESS THAN (15000000000),
PARTITION range_100 values LESS THAN (16000000000),
PARTITION range_110 values LESS THAN (17000000000),
PARTITION range_120 values LESS THAN (18000000000),
PARTITION range_130 values LESS THAN (19000000000),
PARTITION range_140 values LESS THAN (20000000000),
PARTITION range_150 values LESS THAN (21000000000),
PARTITION range_160 values LESS THAN (22000000000),
PARTITION range_mx values LESS THAN (maxvalue)
)
/
あなたが説明したシナリオを再現するのは非常に簡単です。クエリのすべての行はパーティションrange_80
にありますが、Explainプランはすべてのパーティションの検索を示しています。
SQL> explain plan for
select * from t23
where start_date between to_date('31-dec-1390')
and to_date('01-jan-1393')
and start_value between 1050 and 4999
/
2 3 4 5 6
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 4042841927
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 25 | 250 (1)| 00:00:03 | | |
| 1 | PARTITION RANGE ALL| | 1 | 25 | 250 (1)| 00:00:03 | 1 | 17 |
|* 2 | TABLE ACCESS FULL | T23 | 1 | 25 | 250 (1)| 00:00:03 | 1 | 17 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("START_VALUE"<=4999 AND "START_DATE">=TO_DATE(' 1390-12-31 00:00:00',
'syyyy-mm-dd hh24:mi:ss') AND "START_DATE"<=TO_DATE(' 1393-01-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss') AND "START_VALUE">=1050)
16 rows selected.
SQL>
したがって、2つのオプションがあります。最初のオプションは、クエリでSTART_DATE_VALUEを使用することです。おそらく、この明白な解決策を適用しない理由があるでしょう。その列にビジネス上の意味がないためと考えられます。
代替策は、パーティション化戦略を変更することです。これは簡単に実現できます。 START_DATE_VALUE列は、基本的にSTART_DATE内にSTART_VALUEで行を並べ替えます。サブパーティション化でも同じ効果が得られます。
これがテーブルT42です。 START_DATEの範囲パーティションとSTART_VALUEの範囲サブパーティションがあります(テンプレート句は同じパーティションを各パーティションに適用します)。
create table t42
(id number not null primary key
, start_date date not null
, start_value number not null
)
PARTITION BY range (start_date)
SUBPARTITION BY range (start_value)
SUBPARTITION TEMPLATE(
SUBPARTITION lowval VALUES LESS THAN (1000) ,
SUBPARTITION medval VALUES LESS THAN (5000) ,
SUBPARTITION highval VALUES LESS THAN (MAXVALUE)
)
(
PARTITION range_10 values LESS THAN (to_date('01-JAN-700')),
PARTITION range_20 values LESS THAN (to_date('01-JAN-800')),
PARTITION range_30 values LESS THAN (to_date('01-JAN-900')),
PARTITION range_40 values LESS THAN (to_date('01-JAN-1000')),
PARTITION range_50 values LESS THAN (to_date('01-JAN-1100')),
PARTITION range_60 values LESS THAN (to_date('01-JAN-1200')),
PARTITION range_70 values LESS THAN (to_date('01-JAN-1300')),
PARTITION range_80 values LESS THAN (to_date('01-JAN-1400')),
PARTITION range_90 values LESS THAN (to_date('01-JAN-1500')),
PARTITION range_100 values LESS THAN (to_date('01-JAN-1600')),
PARTITION range_110 values LESS THAN (to_date('01-JAN-1700')),
PARTITION range_120 values LESS THAN (to_date('01-JAN-1800')),
PARTITION range_130 values LESS THAN (to_date('01-JAN-1900')),
PARTITION range_140 values LESS THAN (to_date('01-JAN-2000')),
PARTITION range_150 values LESS THAN (to_date('01-JAN-2100')),
PARTITION range_160 values LESS THAN (to_date('01-JAN-2200')),
PARTITION range_mx values LESS THAN (maxvalue)
)
/
何もないことを証明するために、T42にT23のまったく同じデータを入力します。
SQL> insert into t42
select id, start_date, start_value
from t23
/
2 3 4
232800 rows created.
SQL> commit;
Commit complete.
SQL> EXEC DBMS_STATS.gather_table_stats(USER, 'T42')
PL/SQL procedure successfully completed.
SQL> explain plan for
select * from t42
where start_date between to_date('31-dec-1390')
and to_date('01-jan-1393')
and start_value between 1050 and 4999
/
SQL> SQL> 2 3 4 5 6
Explained.
SQL>
ご覧のとおり、explain planは単一のパーティション内の単一のサブパーティションを選択します。精密剪定!
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 2460612001
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 27 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE | | 1 | 17 | 27 (0)| 00:00:01 | 8 | 8 |
| 2 | PARTITION RANGE SINGLE| | 1 | 17 | 27 (0)| 00:00:01 | 2 | 2 |
|* 3 | TABLE ACCESS FULL | T42 | 1 | 17 | 27 (0)| 00:00:01 | 23 | 23 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("START_VALUE"<=4999 AND "START_DATE">=TO_DATE(' 1390-12-31 00:00:00',
'syyyy-mm-dd hh24:mi:ss') AND "START_DATE"<=TO_DATE(' 1393-01-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss') AND "START_VALUE">=1050)
17 rows selected.
SQL>
「サブパーティションを使いたくないのは、テキストインデックスが原因です。」
関連する質問は、サブパーティションが必要ですか?つまり、テーブル(行数)はどのように選ぶのですか? 1日あたりの行数は? START_DATE日あたり1つのパーティションのみを使用してスクレイピングし、START_VALUEを忘れてしまいませんか?