2

履歴データを含む複数のテーブルがあるため、id 間に 1 対 1 の関係はありません。

ID と、データがアクティブになったことを示すタイムスタンプに参加する必要があります。データがまだアクティブである場合、または古いデータに設定されていない場合、TO_TIMESTMP は null になる可能性があります。

いくつかのグループ化後の私のメインテーブルは、次のようなものを出力します:

TABLE_A
AID   USER_ID   AMOUNT  FROM_TIMESTMP        TO_TIMESTMP         
1     1         2       11/21/2012 00:00:00  12/04/2012 11:59:00  
1     2         3       11/24/2012 12:00:00  null                 
2     1         2       11/21/2012 01:00:00  null                 

次に、さらにリンクするために使用する別のテーブルがあります

TABLE_B
AID   CID   FROM_TIMESTMP        TO_TIMESTMP          HIST_ID
1     3     11/01/2012 00:00:00  null                 1
1     3     11/21/2012 00:00:00  12/04/2012 11:59:00  2
1     3     11/24/2012 12:00:00  null                 3
2     4     11/21/2012 00:59:59  null                 4

私の3番目のテーブルは次のようになります。

TABLE_C
CID   VALUE   FROM_TIMESTMP        TO_TIMESTMP          HIST_ID
3     A       11/01/2012 00:00:00  null                 1
3     B       11/21/2012 00:00:00  11/24/2012 11:59:00  2
3     C       11/24/2012 12:00:00  null                 3
4     D       11/21/2012 01:00:01  null                 4

テーブル A をテーブル C からテーブル B までの値と組み合わせたい場合の期待される出力は次のとおりです。

AID   USER_ID  AMOUNT  FROM_TIMESTMP        TO_TIMESTMP          VALUE
1     1        2       11/21/2012 00:00:00  12/04/2012 11:59:00  B
1     2        3       11/24/2012 12:00:00  null                 C
2     1        2       11/21/2012 01:00:00  null                 D

テーブル A の AMOUNT とテーブル C の VALUE を除くすべてにインデックスがあり、次の SQL を使用してデータを引き出します。

SELECT a.AID, a.USER_ID, a.AMOUNT, a.FROM_TIMESTMP, a.TO_TIMESTMP, c.VALUE from 
(SELECT AID, USER_ID, SUM(AMOUNT), FROM_TIMESTMP, TO_TIMESTMP from TABLE_A GROUP BY AID, USER_ID, FROM_TIMESTMP, TO_TIMESTMP) a
inner join TABLE_B b on b.HIST_ID in (select max(HIST_ID) from TABLE_B
where AID = a.AID and FROM_TIMESTMP <= a.FROM_TIMESTMP+1/2880 and (TO_TIMESTMP>= a.FROM_TIMESTMP or TO_TIMESTMP is null))
inner join TABLE_C c on c.HIST_ID in (select max(HIST_ID) from TABLE_C
where CID = b.CID and FROM_TIMESTMP <= a.FROM_TIMESTMP+1/2880 and (TO_TIMESTMP>= a.FROM_TIMESTMP or TO_TIMESTMP is null));

データが保存されるタイミングに矛盾があるため、開始タイムスタンプがほぼ同時に作成された場合に備えて、開始タイムスタンプを比較するときに 30 秒の猶予期間を追加しました。これを改善する方法はありますか?

MAX(HIST_ID) のものを選択するので、TABLE_A の AID=1 や USER_ID=2 のようなケースは、他のテーブルからの id/timestamp に一致する最新の行のみを取得します。

私の実際のデータでは、このように 4 つのテーブル (2 つだけではなく) をインナー ジョインし、ローカル テスト データでうまく機能します (すべてのデータを要求するときに、11 秒で 42000 行をわずかに超えます)。

しかし、データ量が本番に近いテスト環境で実行しようとすると、FROM_TIMESTMP を 2 つの日付の間に設定することで、最初のテーブルでクエリする行の量を約 6000 行に制限しても、実行が遅くなります。

別の方法でテーブルの結合のパフォーマンスを向上させる方法はありますか?

4

2 に答える 2

2

max() サブクエリの繰り返しを避けるための簡単な変更の 1 つは、次のとおりです。

select a.aid,a.user_id,a.amount,a.from_timestmp,a.to_timestmp,a.value
  from (select a.aid,a.user_id,a.amount,a.from_timestmp,a.to_timestmp,c.value, 
       row_number() over (partition by a.aid,a.user_id order by b.hist_id desc, c.hist_id desc) rn
  from (select aid,user_id,sum(amount) amount,from_timestmp,to_timestmp
          from table_a
         group by aid,user_id,from_timestmp,to_timestmp) a
       inner join table_b b
               on b.aid = a.aid
              and b.from_timestmp <= a.from_timestmp + (1 / 2880)
              and ( b.to_timestmp >= a.from_timestmp or b.to_timestmp is null)
       inner join table_c c
               on c.cid = b.cid
              and c.from_timestmp <= a.from_timestmp + (1 / 2880)
              and ( c.to_timestmp >= a.from_timestmp or c.to_timestmp is null)) a
 where rn = 1
  order by a.aid, a.user_id;
于 2012-12-07T11:34:20.883 に答える
1

ある環境ではクエリが速く実行され、別の環境では遅く実行される理由は多数あります。おそらく、オプティマイザが 2 つの異なる計画を定義し、1 つがより高速に実行されることが原因です。おそらく、統計がわずかに異なるためです。

クエリを最適化してインデックスを使用することは確かにできますが、主な問題はデータやデータ モデルにあると思います。そして、データが悪いと、この種の問題に何度も遭遇します。

データを同じテーブルにアーカイブすることは非常に一般的です。これは、履歴を照会する必要がある一時的なデータを表すのに役立ちます。ただし、データをアーカイブしたからといって、データベース設計に関する重要なルールを忘れてはなりません。

あなたの場合、3 つの関連するテーブルがあるようです。それらはエンティティ関係モデルでリンクされます。ただし、設計プロセスのどこかで、このリンクが失われたため、どの行がどの行に依存しているかを確実に特定できなくなりました。

次のことをお勧めします。

  • ER モデルで 2 つのテーブルが関連付けられている場合は、外部キーを追加します。これにより、必要に応じていつでも参加できるようになります。外部キーは、DML 操作でわずかなコストを追加するだけです (そして、INSERT、DELETE、主キーへの更新のみ (?!))。データが 1 回挿入され、何度もクエリされる場合、パフォーマンスへの影響はごくわずかです。

    あなたの場合、 ( AID, FROM_TIMESTAMP) が の主キーである場合、参照の主キー列TABLE_Aに同じ列があります。and ifと(無関係に見える) には異なる更新スキームが必要な場合があります。TABLE_BTABLE_AFROM_TIMESTAMP_AFROM_TIMESTAMP_CAC

  • このロジックに従わない場合は、別の方法でクエリを作成する必要があります。A、B、および C がそれぞれ歴史的にアーカイブされているが、完全に参照されていない場合は、「TS 時の DB のステータスはどうでしたか」などの質問に、単一の時点を参照してのみ回答できます。

    SELECT *
      FROM A
      JOIN B on A.aid = B.aid
      JOIN C on C.cid = B.cid
     WHERE a.timestamp_from <= :TS
       AND nvl(a.timestamp_to, DATE '9999-12-31') > :TS 
       AND b.timestamp_from <= :TS
       AND nvl(b.timestamp_to, DATE '9999-12-31') > :TS
       AND c.timestamp_from <= :TS
       AND nvl(c.timestamp_to, DATE '9999-12-31') > :TS
    
于 2012-12-07T13:16:51.787 に答える