編集注:コメントで述べたように、十分な場所を取得したら、経度/緯度の値を保存し、代わりにそれらに基づいてその場で距離を計算する方が理にかなっている場合があります。ただし、ここで説明するソリューションは、他のアプリケーションにも当てはまる場合があります。
これを処理する最善の方法は、各行に 2 つの場所 ID と距離値があるピボット テーブルを使用することです。
ここで、距離 AB は BA と同じであるため、各ペアを一度だけ保存する必要があります。これを行うには、A の ID が B より小さい場合にのみ距離を保存します。
設定
まず、places
場所を保管するテーブル
id | name
---+---------
1 | Place_A
2 | Place_B
3 | Place_C
4 | Place_D
次に、places_distances
ピボット テーブル:
place_id_1 | place_id_2 | distance
-----------+------------+----------
1 | 2 | 10.0
1 | 3 | 20.0
1 | 4 | 15.0
2 | 3 | 12.0
2 | 4 | 8.0
3 | 4 | 14.0
ピボット テーブルは独自の ID フィールドを必要としないことに注意してください (ただし、場合によっては ID フィールドを持つ方がよいと主張する人もいるかもしれません)。次のように一意のキーを設定します (正しい使用方法についてはドキュメントを参照してください)。
UNIQUE KEY `UNIQUE_placesDistances_primary`(`place_id_1`,`place_id_2`)
これにより、同じ場所/場所のペアリングがテーブルに 2 回存在することがなくなります。
また、外部キーを確実に設定する必要があります。
CONSTRAINT FOREIGN KEY `FK_placesDistances_place1` (`place_id_1`)
REFERENCES `places`(`id`),
CONSTRAINT FOREIGN KEY `FK_placesDistances_place2` (`place_id_2`)
REFERENCES `places`(`id`)
これにより、 で実際に定義した場所のエントリのみを追加できるようになりますplaces
。また、(デフォルトの外部キー動作を使用する場合) その場所を参照する距離行がある場合、その場所を削除できないことも意味します。
使用例
2地点間の距離を見上げる
@id_1
( 1位のidと2位のidとして2つの変数を与える@id_2
)
SELECT `distance`
FROM `places_distances`
WHERE (`place_id_1` = @id_1 AND `place_id_2` = @id_2)
OR (`place_id_2` = @id_1 AND `place_id_11` = @id_2)
LIMIT 1;
2
OR を使用して、距離を検索しようとするケースを説明します。重複の保存を避けるために、1 番目の場所の ID が 2 番目よりも小さい値のみを保存することを覚えておいて1
ください1
。2
新しい距離の挿入
@id_1
( 1位@id_2
のidと2位のid @distance
、距離となる変数を3つ与えた場合)
INSERT `places_distances`(`place_id_1`,`place_id_2`,`distance`)
VALUES(LEAST(@id_1, @id_2),GREATEST(@id_1, @id_2), @distance)
組み込みの比較関数 LEAST
を使用しGREATEST
ており、重複を避けるために、最初の ID が 2 番目より小さい場所のみを保存するというルールを維持するのに役立ちます。
地名のリストを、最も遠いものから最も近いものへの距離で並べ替えて表示しています
places
テーブルから元の名前を取得してplaces_distances
クエリに表示するには、それらを結合する必要があります。この場合、テーブルLEFT JOIN
の内容のみを気にするため、これが最良の選択です。MySQL 結合の詳細については、こちらを確認してください。places_distances
SELECT
`p_1`.`name` AS `place_1`,
`p_2`.`name` AS `place_2`,
`distance`
FROM `places_distances`
LEFT JOIN `places` AS `p_1`
ON `distances`.`place_id_1` = `p_1`.`id`
LEFT JOIN `places` AS `p_2`
ON `distances`.`place_id_2` = `p_2`.`id`
ORDER BY `distance` DESC
次のようなテーブルを返す必要があります。
place_id_1 | place_id_2 | distance
-----------+------------+----------
Place_A | Place_C | 20.0
Place_A | Place_D | 15.0
Place_C | Place_D | 14.0
Place_B | Place_C | 12.0
Place_A | Place_B | 10.0
Place_B | Place_D | 8.0
場所と特定の場所までの距離の表を表示する
入力場所ではない行に名前を表示する必要があるため、これは少しトリッキーですが、別の便利な関数IF(CONDITION,'TRUE_OUTPUT','FALSE_OUTPUT')
を使用してそれを行うことができます。
(@place_name
場所名を含む変数、この場合は「Place_B」)
SELECT
IF(`p_1`.`name`=@place_name, `p_2`.`name`, `p_1`.`name`) AS `name`,
`distance`
FROM `places_distances`
LEFT JOIN `places` AS `p_1`
ON `distances`.`place_id_1` = `p_1`.`id`
LEFT JOIN `places` AS `p_2`
ON `distances`.`place_id_2` = `p_2`.`id`
WHERE `p_1`.`name` = @place_name OR `p_2`.`name` = @place_name
ORDER BY `distance` DESC
次のようなテーブルを返す必要があります。
name | distance
--------+-----------
Place_C | 12.0
Place_A | 10.0
Place_D | 8.0