2016-11-02 8 views
3

私たちは、データベースに一定期間の間のみ関連性のある/有効なテーブルをたくさん用意しています。契約の例として、start_dateとend_dateがあります。そして、それは必ずしも完全な月ではありません。タイムスライスを見つけるためにデータ/インデックスをモデル化する方法

さて、これは、このテーブルに対するクエリの典型的なタイプです:

SELECT 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

私たちは、テーブル内のデータの20年(〜7000日)を持っているので、日付が場合は特に、非常に優れたフィルタ基準であります: 1と:2は同じ日です。 region_idはあまり良いフィルタ基準ではありません(〜50)。オプティマイザは、それが安いだと思っているため残念ながら

contracts_valid_index (start_date, end_date) 
contracts_region (region_id) 

、クエリ以上になることが多い私たちcontracts_regionインデックス:この例では、(特に)私たちのテーブルの上に2つのインデックスを持っています。これは、データの途中で1日を選択すると、データの半分だけをフィルタリングするため、start_dateのインデックスは実際にはうまくいかないとデータベースは考えます。 end_dateを見ることで同じことが言えます。したがって、オプティマイザは自分のデータの1/4だけをフィルタリングできると考えています。なぜなら、彼はstart_dateとend_dateが通常はかなり接近していることを知らないので、このインデックスは非常に選択的です。

contracts_valid_indexを使用する実行計画の方が、contracts_regionを使用する実行計画よりもコストが高くなります。しかし、実際にはcontracts_valid_indexはずっと優れています。

私は現在、よりよいインデックスを作成してクエリを高速化できるとは思わない(contracts_valid_index以外のすべてを削除する以外に)。しかし、おそらく私のデータモデルはクエリオプティマイザにとってあまり良くありません。だから私は、他の人たちも同様のニーズを抱えていて、データのモデル化やデータテーブル/インデックスの最適化方法を知りたいと思っています。

提案がありますか?

答えて

1

Oracle 12cを使用していることを示しているので、Start_Date列とEnd_Date列を適切な時間的有効性セマンティクス(start_dateとend_dateはタイムスタンプにする必要があり、end_dateはstart_dateにする必要があります。おそらくヌルで、有効期間には開始日が含まれますが、終了日は除外されます。つまり、完全に閉じた範囲を表す通常の演算子とは異なり、部分的に閉じた/開いた範囲です。たとえば:

ALTER TABLE contracts ADD (PERIOD FOR valid_time (start_date, end_date)); 

その後、thusly与えられた有効期間のための契約のテーブルを照会することができます

SELECT 
    c.* 
FROM 
    contracts VERSIONS PERIOD FOR valid_time BETWEEN :1 AND :2 c 
WHERE 
    c.region_id = :3 

これは、意味的に似ています。そのレコードを照会する

また
SELECT 
    c.* 
FROM 
    contracts c 
WHERE 
     :1 < end_date 
    AND start_date <= :2 
    AND c.region_id = :3 

時間の範囲ではなく特定の時点で有効です。

SELECT 
    c.* 
FROM 
    contracts AS OF PERIOD FOR valid_time :1 c 
WHERE 
    c.region_id = :2 
私は現在、テストにR12のインスタンスを持っていないので、START_DATEとEND_DATEのためにnull値がそれぞれたりない時間の始まりと終わりを示すかどうかわからないんだけど

SELECT 
    c.* 
FROM 
    contracts c 
WHERE 
     :1 BETWEEN start_date AND end_date 
    and :1 <> end_date 
    and c.region_id = :2 

:意味的に類似している

に。

+0

これは私が探していたものです。私はインターバルの終わりが気に入らないが、それは問題ではない。しかし、私はこれについてJPAのサポートを受けるつもりはない – EasterBunnyBugSmasher

+0

これを投稿した後にもう少しリサーチを行ったところ、開始日と終了日の列は日付またはタイムスタンプのいずれかになり、NULL値は時間の始めと終わりとして扱われますそれらの値にマジックの日付を使用すると、彼らは引き続き動作します。 – Sentinel

+0

オープンとクローズド・エンドの範囲に関しては、私は両方で働いてきており、オープン・エンドの方がはるかに優れています。そのようにして1つの範囲が終わり、次の範囲が始まると、次の開始と同じようにプリオーダーの終わりに同じ日付を使うことができ、重なりの心配はありません。開始日と終了日に切り捨てられた値を使用している場合、1つの範囲の終了と次の開始の間のギャップに収まる範囲外の範囲チェックを心配する必要はありません。 – Sentinel

1

私は以前、MySQLデータベース上の大規模なIPアドレスのセットと関連して同じインデックスの問題を抱えていました。

The solutionジオスペースインデックスを使用すると、(私は多くのグーグルでそれを発明しているとは言えません)これは、範囲内のデータを見つけるために特別に設計されています。ほとんどの実装(mysqlの実装を含む)は2次元空間に固定されていますが、IPアドレスと時間は1次元ですが、1次元座標を2次元空間にマッピングするのは簡単です(ステップの説明のリンクを参照してください) 。

オラクルの地理空間機能についてはわかりませんので、サンプルコードは提供できませんが、地理空間索引付けをサポートしているため、問合せを効率的に解決できます。

0

あなたはそれがよりよく動作するかどうかを確認するには、次の問合せを試みることができる:

WITH t1 AS (
    SELECT * 
    FROM contracts c 
    WHERE c.start_date <= :1 
     AND c.end_date >= :2 
) 
SELECT * 
    FROM t1 
    WHERE c.region_id = :3 

それはおそらくcontracts_regionインデックスを使用しての可能性を防ぐことができますけど。望ましくないインデックスを使用しないように

SELECT /*+ INDEX(c contracts_valid_index) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

か、それをほのめかし:

別の方法としては、目的のインデックスを使用するクエリをほのめかしてみてください

SELECT /*+ NO_INDEX(c contracts_region) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

ヒントを使用せずに、自分のためにこれをテスト利用可能な日付範囲の開始日または終了日付近の日付を選択すると、オプティマイザはINDEX_RS_ASCヒントを使用していることがわかりました。

SELECT /*+ INDEX_RS_ASC(c contracts_valid_index) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

マイサンプルデータが均等に50の領域accross分散10,000,000行から構成:以下に示すようにクエリにそれを私のテストは、日付範囲は、日付範囲の中心に近いあった場合でも、所望のインデックスを使用させる添加します有効期間は30日間です。

関連する問題