私はこのGTFSデータベースを持っています:
すべての***_id
列にインデックスが付けられ、テーブル間に外部キーがあります(trips.route_id
からroutes.route_id
へのFKなど)。
したがって、基本的にストップとルート間のリンクはroutes
→trips
→stop_times
→stops
です。
私は、物理的に互いに近いストップをすべて記載するテーブルを作成して埋め、stop_connections
を埋めました。ものすごく単純。
ここで私が欲しいのは、ルートAから取得することです。このルートのストップの1つから到達できる他のすべてのルート。
だから基本的に私は次のように行きたいです:
A.route → A.trips (many) → A.trips.stop_times(many^2) → A.trips.stop_times.stops(many^3)
次に、stop_connections
を使用して関連するストップを取得し、逆戻りしてこれらのストップからルートを取得します。
connected_stops → connected_stops.stop_times → connected_stops.stop_times.trips → connected_stops.stop_times.trips.routes
私はこれに似たテーブルを得るでしょう:
+---------------+-------------+
| from_route_id | to_route_id |
+---------------+-------------+
| 0001 | 0002 |
| 0001 | 0004 |
| 0001 | 0005 |
| 0002 | 0001 |
| 0002 | 0005 |
+---------------+-------------+
これは私のクエリです:
-- get from/to route IDs
select
r.route_id as from_route_id,
c_t.route_id as to_route_id
- start from route A
from routes r
-- going down on all trips on route A
left join trips t on t.route_id = r.route_id
-- going down on all stop_times for A's trips
left join stop_times st on st.trip_id = t.trip_id
-- going down on all stops use by A's trips/vehicles
left join stops s on s.stop_id = st.stop_id
-- get connected stops
inner join stop_connections c_s on c_s.from_stop_id = s.stop_id
-- going up to stop_times
inner join stop_times c_st on c_st.stop_id = c_s.to_stop_id
-- going up to trips
inner join trips c_t on c_t.trip_id = c_st.trip_id and c_t.route_id <> r.route_id
-- no need to actually go up to routes because we already have the route ID
-- I just want every from/to routes mentioned only once!
group by r.route_id, c_t.route_id -- this is my problem here
ここに説明があります:
+----+-------------+-------+--------+------------------------------------------+------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+------------------------------------------+------------------+---------+----------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | r | const | PRIMARY | PRIMARY | 302 | const | 1 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | t | ref | PRIMARY,trip_route_id | trip_route_id | 302 | const | 1474 | Using where; Using index |
| 1 | SIMPLE | st | ref | st_trip_id,st_stop_id | st_trip_id | 302 | bicou_gtfs_rennes.t.trip_id | 14 | Using where |
| 1 | SIMPLE | c_s | ref | from_to_stop_ids,from_stop_id,to_stop_id | from_to_stop_ids | 767 | bicou_gtfs_rennes.st.stop_id | 1 | Using where; Using index |
| 1 | SIMPLE | s | eq_ref | PRIMARY | PRIMARY | 767 | bicou_gtfs_rennes.st.stop_id | 1 | Using where; Using index |
| 1 | SIMPLE | c_st | ref | st_trip_id,st_stop_id | st_stop_id | 302 | bicou_gtfs_rennes.c_s.to_stop_id | 325 | Using where |
| 1 | SIMPLE | c_t | eq_ref | PRIMARY,trip_route_id | PRIMARY | 767 | bicou_gtfs_rennes.c_st.trip_id | 1 | Using where |
+----+-------------+-------+--------+------------------------------------------+------------------+---------+----------------------------------+------+----------------------------------------------+
このクエリを完了することができませんでした。 500、5000、50k、または100kの制限でこれは30秒未満で行われますが、行の総数がわからないので、数百万を期待します(rows
ステートメントのすべてのEXPLAIN
の積ですか?私はクエリの最適化でかなり新しいです)。
制限付きで実行しているとき、SQLエンジンは行があると停止しますが、これは私が取得するものです。
+---------------+-------------+
| from_route_id | to_route_id |
+---------------+-------------+
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
| 0001 | 0150 |
+---------------+-------------+
10 rows in set (0.19 sec)
だから私は(上のクエリにある)グループを追加すると思っていましたが、今度はSQLエンジンがすべての行を調べてそれらを並べ替える必要があり、これには時間がかかります。
また、さまざまな結合方法を試しました。最初に左結合、次に内部結合、そしてSTRAIGHT_JOIN
です。これはどれも役に立たなかった。
これの目的はルックアップテーブルに入力することなので、そのクエリに数分かかる場合は問題ありません。しかし、私は1日中CPUとディスクを狂ったように使用しないようにしたいと思います。私は毎月かそこらでこのクエリを必要とする予定です。
MySQL 5.1.66-0+squeeze1
を使用しています。
OK、私は別のルックアップテーブルを追加してしまいました。
CREATE TABLE IF NOT EXISTS `stops_routes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stop_id` varchar(100) NOT NULL,
`route_id` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `stop_route` (`stop_id`,`route_id`),
KEY `stop_id` (`stop_id`),
KEY `route_id` (`route_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
それを満たすことはかなり高速でした:
mysql> insert into stops_routes (stop_id, route_id)
->
-> select
-> s.stop_id,
-> r.route_id as from_route_id
->
-> from routes r
-> left join trips t on t.route_id = r.route_id
-> left join stop_times st on st.trip_id = t.trip_id
-> left join stops s on s.stop_id = st.stop_id
-> group by s.stop_id, r.route_id;
Query OK, 3496 rows affected (8.38 sec)
Records: 3496 Duplicates: 0 Warnings: 0
それを使用すると、非常に高速です。
mysql> select
-> r.route_id as from_route_id,
-> c_sr.route_id as to_route_id
->
-> from routes r
->
-> left join stops_routes sr on sr.route_id = r.route_id
-> left join stop_connections c_s on c_s.from_stop_id = sr.stop_id
-> left join stops_routes c_sr on c_sr.stop_id = c_s.to_stop_id
->
-> where r.route_id <> c_sr.route_id
-> group by r.route_id, c_sr.route_id
-> limit 10;
+---------------+-------------+
| from_route_id | to_route_id |
+---------------+-------------+
| 0001 | 0002 |
| 0001 | 0003 |
| 0001 | 0004 |
| 0001 | 0005 |
| 0001 | 0006 |
| 0001 | 0008 |
| 0001 | 0009 |
| 0001 | 0011 |
| 0001 | 0014 |
| 0001 | 0031 |
+---------------+-------------+
10 rows in set (0.63 sec)
これで、最後のルックアップテーブル(GTFSネットワーク上のすべてのルート間の接続のセット)に入力できます。
mysql> insert into route_connections (from_route_id, to_route_id)
-> select
-> r.route_id as from_route_id,
-> c_sr.route_id as to_route_id
->
-> from routes r
->
-> left join stops_routes sr on sr.route_id = r.route_id
-> left join stop_connections c_s on c_s.from_stop_id = sr.stop_id
-> left join stops_routes c_sr on c_sr.stop_id = c_s.to_stop_id
->
-> where r.route_id <> c_sr.route_id
-> group by r.route_id, c_sr.route_id;
Query OK, 2848 rows affected (0.31 sec)
Records: 2848 Duplicates: 0 Warnings: 0
驚くほど速い。エンジンはこれを最適化するためのステップを分割できなかったと思います。
1秒未満または1分未満のクエリのみを使用して同じ結果を取得できるかどうか(ルートからルート接続テーブルまで)が知りたいです。