4

次の設定で、3 つのテーブルがあります。

TEMPERATURE_1
  time
  zone (FK)
  temperature
TEMPERATURE_2
  time
  zone (FK)
  temperature
TEMPERATURE_3
  time
  zone (FK)
  temperature

各テーブルのデータは定期的に更新されますが、必ずしも同時に更新されるとは限りません (つまり、時間エントリは同一ではありません)。

毎回各テーブルから最も近い読み取り値にアクセスできるようにしたい、つまり:

TEMPERATURES
  time
  zone (FK)
  temperature_1
  temperature_2
  temperature_3

言い換えれば、3 つのテーブル全体の一意の時間ごとに、TEMPERATURES テーブルに行が必要です。ここで、温度_n の値は、各元のテーブルから時間的に最も近い温度読み取り値です。

現時点では、2 つのビューを使用してこれを設定しています。

create view temptimes
as select time, zone 
  from temperature_1
union
  select time, zone
  from temperature_2
union
  select time, zone
  from temperature_3;

create view temperatures
as select tt.time,
          tt.zone,
          (select temperature 
           from temperature_1
           order by abs(timediff(time, tt.time))
           limit 1) as temperature_1,
          (select temperature 
           from temperature_2
           order by abs(timediff(time, tt.time))
           limit 1) as temperature_2,
          (select temperature 
           from temperature_3
           order by abs(timediff(time, tt.time))
           limit 1) as temperature_3,
from temptimes as tt
order by tt.time;

このアプローチは機能しますが、本番環境で使用するには遅すぎます (温度ごとに最大 1000 レコードの小さなデータ セットの場合、数分以上かかります)。

私はSQLが得意ではないので、これを行う正しい方法が欠けていると確信しています。問題にどのようにアプローチすればよいですか?

4

3 に答える 3

0

これが遅い理由は、差異を計算して順序付けるために3回のテーブルスキャンが必要になるためです。

タイムゾーン列にすでにインデックスがあると思いますが、現時点では、テーブルスキャンの問題のためにインデックスは役に立ちません。

必要なものとデータ収集率に応じて、これを回避するためのオプションがいくつかあります。

データは定期的に収集されますが、同時には収集されないと既におっしゃっています。これはいくつかのオプションを示唆しています。

  1. どのレベルの重要度(日、時間、分など)に必要な一時データが必要ですか。タイムゾーン情報をその重要度レベルにのみ保存し(または別の列を作成して)、クエリを実行します。
  2. 3つのクローゼットの時間が特定の時間枠(時間、日など)内にあることがわかっている場合は、where句を挿入して、計算を潜在的な候補となる時間に制限します。ヒストグラムタイプのバケットを効果的に構築しています。これを効率的に行うには、カレンダーテーブルが必要です。
  3. 比較を一方向にします。つまり、検討対象を探している時間以降の時間のみに制限します。したがって、12:00:00を探している場合は、13:45:32が候補になりますが、11:59:59は候補になりません。

私はあなたが達成しようとしていることを理解しています-理由と、より単純な解決策があなたのニーズを必要とするかどうかを自問してください。

于 2013-02-03T23:55:10.453 に答える
0

私の提案は、最も近い時間を取るのではなく、指定された時間またはそれ以前の最初の時間を取ることです。その理由は簡単です。通常、特定の時間のデータは、その時点でわかっているものです。一般に、将来の情報を組み込むことは、ほとんどの目的にとって適切ではありません。

この変更により、 のインデックスを利用するようにクエリを変更できますtime。クエリのインデックスの問題は、関数がインデックスを使用できないことです。

したがって、最新の温度が必要な場合は、代わりに各変数に次のように使用します。

      (select temperature 
       from temperature_1 t2
       where t2.time <= tt.time
       order by t2.time desc
       limit 1
      ) as temperature_1,

実際には、次のように構築することもできます。

      (select time 
       from temperature_1 t2
       where t2.time <= tt.time
       order by t2.time desc
       limit 1
      ) as time_1,

そして、温度の情報を再び結合します。これは、インデックスを使用して効率的になります。

それを念頭に置いて、実際には 2 つの変数time_1_beforetime_1_afterを使用して、前後のベスト タイムと前後のベスト タイムを表すことができます。選択でロジックを使用して、最も近い値を選択できます。温度への結合は、インデックスを使用して効率的に行う必要があります。

しかし、繰り返しますが、前回の温度が 12 月以前が最良の選択であると思います。

于 2013-02-04T03:17:53.333 に答える
0

コストのかかる部分は、相関サブクエリが各temperature_*テーブルのすべての行の時間差を計算して、メイン クエリの 1 つの行の 1つの列に対して最も近い行を1 つだけ見つける必要があるところです。

インデックスに従って現在時刻の1行後と1行前を選択し、これら 2 つの候補の時間差のみを計算できれば、劇的に高速になります。それを高速にするために必要なのは、テーブルの列のインデックスだけです。time

zone質問ではその役割が不明のままであり、コアの問題にノイズを追加するだけなので、列を無視しています。クエリに簡単に追加できるはずです。

追加のビューがない場合、このクエリはすべてを一度に実行します。

SELECT time
      ,COALESCE(temp1
            ,CASE WHEN timediff(time, time1a) > timediff(time1b, time) THEN
                (SELECT t.temperature
                 FROM   temperature_1 t
                 WHERE  t.time = y.time1b)
             ELSE
                (SELECT t.temperature
                 FROM   temperature_1 t
                 WHERE  t.time = y.time1a)
             END) AS temp1

      ,COALESCE(temp2
            ,CASE WHEN timediff(time, time2a) > timediff(time2b, time) THEN
                (SELECT t.temperature
                 FROM   temperature_2 t
                 WHERE  t.time = y.time2b)
             ELSE
                (SELECT t.temperature
                 FROM   temperature_2 t
                 WHERE  t.time = y.time2a)
             END) AS temp2

      ,COALESCE(temp3
            ,CASE WHEN timediff(time, time3a) > timediff(time3b, time) THEN
                (SELECT t.temperature
                 FROM   temperature_3 t
                 WHERE  t.time = y.time3b)
             ELSE
                (SELECT t.temperature
                 FROM   temperature_3 t
                 WHERE  t.time = y.time3a)
             END) AS temp3
FROM  (
  SELECT time
        ,max(t1) AS temp1
        ,max(t2) AS temp2
        ,max(t3) AS temp3

        ,CASE WHEN max(t1) IS NULL THEN
           (SELECT t.time FROM temperature_1 t
            WHERE  t.time < x.time
            ORDER  BY t.time DESC LIMIT 1) ELSE NULL END AS time1a
        ,CASE WHEN max(t1) IS NULL THEN
           (SELECT t.time FROM temperature_1 t
            WHERE  t.time > x.time
            ORDER  BY t.time      LIMIT 1) ELSE NULL END AS time1b
  
        ,CASE WHEN max(t2) IS NULL THEN
           (SELECT t.time FROM temperature_2 t
            WHERE  t.time < x.time
            ORDER  BY t.time DESC LIMIT 1) ELSE NULL END AS time2a
        ,CASE WHEN max(t2) IS NULL THEN
           (SELECT t.time FROM temperature_2 t
            WHERE  t.time > x.time
            ORDER  BY t.time      LIMIT 1) ELSE NULL END AS time2b

        ,CASE WHEN max(t3) IS NULL THEN
           (SELECT t.time FROM temperature_3 t
            WHERE  t.time < x.time
            ORDER  BY t.time DESC LIMIT 1) ELSE NULL END AS time3a
        ,CASE WHEN max(t3) IS NULL THEN
           (SELECT t.time FROM temperature_3 t
            WHERE  t.time > x.time
            ORDER  BY t.time      LIMIT 1) ELSE NULL END AS time3b
  FROM  (
      SELECT time, temperature AS t1, NULL AS t2, NULL AS t3 FROM temperature_1
      UNION ALL
      SELECT time, NULL AS t1, temperature AS t2, NULL AS t3 FROM temperature_2
      UNION ALL
      SELECT time, NULL AS t1, NULL AS t2, temperature AS t3 FROM temperature_3
      ) AS x
  GROUP BY time
  ) y
ORDER BY time;

->sqlfiddle

説明

suqquery xはビューを置き換えtemptimes、結果に温度をもたらします。3 つのテーブルすべてが同期しており、すべて同じ時点の温度がある場合、残りは必要なく、非常に高速です。
3 つのテーブルのうちの 1 つに行がないすべての時点で、指示に従って温度が取得されます。各テーブルから「最も近い」ものを取得します。

suqquery yは、温度が欠落している各テーブルからの行を集約し、現在の時間に従ってx前回 ( time1a) と次回( ) を取得します。time1bこれらのルックアップは、インデックスを使用して高速にする必要があります。

最後のクエリは、実際には欠落している各気温の最も近い時刻の行から気温を取得します。

MySQL が現在のサブクエリよりも 2 つ以上上のレベルから列を参照できるようになれば、このクエリはより単純になる可能性があります。ちょっとそれはできません。PostgreSQLで問題なく動作します: ->sqlfiddle

また、相関サブクエリから複数の列を返すことができればより簡単になりますが、MySQL でそれを行う方法がわかりません。

また、 CTEウィンドウ関数を使用するとはるかに簡単になりますが、MySQL はこれらの最新の SQL 機能を認識していません (他の関連する RDBMS とは異なります)。

于 2013-02-04T02:39:36.063 に答える