5

場所を含むデータベースがあり、任意の場所から他の場所までの距離を Web ページに表示する必要があります。距離をどこかに保存しておくと、多くの作業を節約できます (それらをロードする方が、新たに計算するよりも簡単なはずです)。しかし、距離の正方行列を保存する方法は? 新しい行を挿入するたびに新しい列を作成することは良い解決策ではないようですが、より良い解決策は見つかりませんでした(ただし、10 または 20 の最も近い距離を計算し、そうすると仮定するなどの回避策を考えることができます)それ以上必要になることはめったにありません)。

PHP/MySQLで可変(および成長中)サイズの正方形のテーブルを保存する最適な方法は何ですか? または、良い解決策がなく、私の(または他の)回避策が優れていますか?

4

6 に答える 6

5

編集注:コメントで述べたように、十分な場所を取得したら、経度/緯度の値を保存し、代わりにそれらに基づいてその場で距離を計算する方が理にかなっている場合があります。ただし、ここで説明するソリューションは、他のアプリケーションにも当てはまる場合があります。


これを処理する最善の方法は、各行に 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;

2OR を使用して、距離を検索しようとするケースを説明します。重複の保存を避けるために、1 番目の場所の ID が 2 番目よりも小さい値のみを保存することを覚えておいて1ください12


新しい距離の挿入

@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
于 2013-09-13T13:59:14.180 に答える
3

すべての場所の緯度/経度を保存し、緯度/経度情報を使用してそれらの間の距離を計算する関数を作成します。

このように、DB に追加する新しい場所の距離を計算する必要はありません。

さらに、ピボット テーブルを使用して距離のみを保存する場所が多数ある場合、このテーブルは非常に急速に大きくなる可能性があることに注意する必要があります。場所のすべての組み合わせをカバーする必要があるため。

例: 1000 の場所の場合、テーブルには 1000 * 1000 - 1000 = 999000 行があります。より大きな数の計算を行いますが、このテーブルには多くの行が含まれている可能性があり、取得した場所の数によって異なります。

于 2013-09-13T12:24:15.270 に答える