3

次の表を考えます。

Table events
id
start_time
end_time

定数をすばやく検索する方法はありますか?

例えば

SELECT *
FROM events
WHERE start_time<='2009-02-18 16:27:12' 
AND     end_time>='2009-02-18 16:27:12'

私はMySQLを使用しています。いずれかのフィールドにインデックスがある場合でも、範囲をチェックする必要があります。さらに、両方のフィールドにインデックスを付けても違いはありません (最初のフィールドのみが使用されます)。

フィールド/インデックスをテーブルに追加できます (したがって、両方のフィールドの情報を含むインデックス付きの構築フィールドを追加することは許容されます)。

PSこれの必要性は、この質問から来ました:句の間を使用するSQLを最適化する

4

6 に答える 6

6

私の解決策には1つの警告があります:

1) このソリューションの注意点は、イベント テーブルに MyISAM エンジンを使用する必要があることです。MyISAM を使用できない場合、空間インデックスでは MyISAM のみがサポートされているため、このソリューションは機能しません。

したがって、上記が問題にならないと仮定すると、以下が機能し、良好なパフォーマンスが得られるはずです。

このソリューションは、MySQL の Spatial Data のサポートを利用します (こちらのドキュメントを参照してください)。空間データ型はさまざまなストレージ エンジンに追加できますが、必要なパフォーマンスを得るために必要な空間 R ツリー インデックス (こちらのドキュメントを参照) では MyISAM のみがサポートされています。もう 1 つの制限は、空間データ型は数値データでのみ機能するため、この手法を文字列ベースの範囲クエリで使用できないことです。

空間タイプがどのように機能するか、および空間インデックスがどのように役立つかの背後にある理論の詳細については説明しませんが、GeoIP ルックアップに空間データ タイプとインデックスを使用する方法については、 Jeremy Cole の説明を参照してください。また、生のパフォーマンスが必要で、ある程度の精度が必要な場合は、コメントがいくつかの有用なポイントと代替案を提起しているため、コメントを参照してください。

基本的な前提は、始点/終点を取得し、そのうちの 2 つを使用して、xy グリッド上の 0,0 を中心とする長方形の各コーナーに 1 つずつ、4 つの異なるポイントを作成し、空間をすばやく検索できることです。気になる特定の時点が長方形内にあるかどうかを判断するためのインデックス。前述のように、これがどのように機能するかのより完全な概要については、Jeremy Cole の説明を参照してください。

特定のケースでは、次のことを行う必要があります。

1) テーブルを MyISAM テーブルに変更します (MyISAM に関連するトランザクションの欠如やテーブルのロック動作など、このような変更の結果を十分に認識していない限り、これを行うべきではないことに注意してください)。

alter table events engine = MyISAM;

2) 次に、空間データを保持する新しい列を追加します。完全な長方形を保持できるようにする必要があるため、ポリゴン データ型を使用します。

alter table events add column time_poly polygon NOT NULL;

3) 次に、新しい列にデータを入力します (テーブル イベントに更新または挿入するプロセスは、新しい列にも入力されるように変更する必要があることに注意してください)。開始範囲と終了範囲は時間であるため、unix_timestamp 関数を使用して数値に変換する必要があります (仕組みについては、こちらのドキュメントを参照してください)。

update events set time_poly := LINESTRINGFROMWKB(LINESTRING(
    POINT(unix_timestamp(start_time), -1),
    POINT(unix_timestamp(end_time), -1),
    POINT(unix_timestamp(end_time), 1),
    POINT(unix_timestamp(start_time), 1),
    POINT(unix_timestamp(start_time), -1)
  ));

4) 次に、空間インデックスをテーブルに追加します (前述のように、これは MyISAM テーブルでのみ機能し、「エラー 1464 (HY000): 使用されているテーブル タイプは SPATIAL インデックスをサポートしていません」というエラーが発生します)。

alter table events add SPATIAL KEY `IXs_time_poly` (`time_poly`);

5) 次に、データのクエリを実行するときに空間インデックスを利用するために、次の選択を使用する必要があります。

SELECT * 
FROM events force index (IXs_time_poly)
WHERE MBRCONTAINS(events.time_poly, POINTFROMWKB(POINT(unix_timestamp('2009-02-18 16:27:12'), 0)));

強制インデックスは、MySQL がルックアップにインデックスを使用することを 100% 確実にするためにあります。すべてがうまくいった場合、上記の select で Explain を実行すると、次のようなものが表示されます。

mysql> explain SELECT *
    -> FROM events force index (IXs_time_poly)
    -> on MBRCONTAINS(events.time_poly, POINTFROMWKB(POINT(unix_timestamp('2009-02-18 16:27:12'), 0)));
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
|  1 | SIMPLE      | B     | range | IXs_time_poly | IXs_time_poly | 32      | NULL |    1 | Using where | 
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
1 row in set (0.00 sec)

between 句と比較した場合のこの方法のパフォーマンス上の利点の詳細については、Jeremy Cole の分析を参照してください。

ご不明な点がございましたら、お知らせください。

ありがとう、

-ディピン

于 2009-02-19T20:02:05.517 に答える
2

でこのクエリを正確に実行する効率的な方法はありませんMySQL

ただし、範囲が重複していない場合は、とstart_time <= const一緒に使用してORDER BY start_time DESC LIMIT 1、さらにチェックすることができますend_time >= const

範囲条件がスーパークエリから取得された場合、何らかの理由でサブクエリでforをMySQL使用しないため、関数でそれを行う必要があります。INDEX RANGE SCANORDER BY

CREATE UNIQUE INDEX ux_b_start ON b (start_date);

CREATE FUNCTION `fn_get_last_b`(event_date TIMESTAMP) RETURNS int(11)
BEGIN
  DECLARE id INT;
  SELECT b.id
  INTO id
  FROM b
  FORCE INDEX (ux_b_start)
  WHERE b.start_time <= event_date
  ORDER BY
    b.start_time DESC
  LIMIT 1;
  RETURN id;
END;

SELECT COUNT(*) FROM a;

1000


SELECT COUNT(*) FROM b;

200000

SELECT *
FROM (
  SELECT fn_get_last_b(a.event_time) AS bid,
         a.*
  FROM a
) ao, b FORCE INDEX (PRIMARY)
WHERE b.id = ao.bid
  AND b.end_time >= ao.event_time

1000 rows fetched in 0,0143s (0,1279s)
于 2009-02-18T14:50:41.210 に答える
0

基本的に、2 つの明確に異なる範囲条件を持つクエリがあります。>= を使用しています。MySQL では、これは常に範囲スキャンです。範囲スキャンを最適化するためのドキュメントがここにあります。

一番下の行は、MySQL が追加のチェックを実行して範囲条件を満たす行を除外し、残りの WHERE 句 (この場合は別の範囲条件) を満たすことです。

于 2009-02-18T22:22:45.227 に答える
0

イベント (開始時間と終了時間のあるアイテム) の検索の最適化について同様の質問をするつもりでしたが、既に別のアプローチを使用しているので、そこに投げます。

基本的に、イベントが特定の期間を超えることがないことがわかっている場合は、最大期間よりも大きい境界範囲を検索し、制限を追加して、一致した余分なものを取り除くことができます。したがって、検索時間と交差する時間を取得するには:

SELECT *
FROM events
WHERE 
   ( start_time BETWEEN ( 'search_start' - INTERVAL 2 DAY ) and 'search_end' )
   AND end_time >= 'search_start'

... にインデックスを付けたいと思うでしょうstart_time

(注 - 私のテーブルには 4 年間にまたがる数百万のイベントがあり、24 時間以上の記録はありません...自分で試してみる必要があるため、空間検索アプローチと比較してこれがどのように機能するかわかりません.)

于 2010-01-07T18:36:57.000 に答える
0

MySQL の経験はあまりありませんが、MS SQL Server では、両方の列にインデックスを追加すると、1M 行のテーブルでのインデックスのシーク時間と戻り時間が 1 ~ 2 秒からミリ秒の応答時間になりました。

さまざまな結果が表示されているようです。制約が違いを生んでいるのだろうか。start_time < end_time を強制するためのチェック制約があります。

于 2009-02-18T15:49:23.283 に答える
-1

1 つのテーブル内でできることはあまりありません。これらのクエリの最適化が 1) 必要であり、2) SQL レベルで実行する必要がある場合は、派生テーブルを作成する必要があります。

Table event_times
id
event_id
mark_time

各イベントがまたがる時間単位ごとにレコードを追加します。それからあなたはただ

SELECT *
FROM events
LEFT JOIN event_times ON event_id = events.id
WHERE mark_time = '2009-02-18 16:27:12'

「時間の単位」をどのように定義するかによって、このテーブルを少しばかげたものにすることができます。つまり、mark_time の解像度を秒ではなく分または時間に制限する場合です。

于 2009-02-18T14:53:30.267 に答える