我有一个包含地点的数据库,我需要在我的网页中显示从任何地方到其他地方的距离。将距离存储在某处将节省大量工作(加载它们应该比重新计算它们更容易)。但是如何保存距离的方阵呢?每次插入新行时创建一个新列似乎不是一个好的解决方案,但我没有找到任何更好的解决方案(虽然我可以想到解决方法,例如计算10或20个最近距离,假设我很少需要更多距离。)
在PHP / MySQL中保存变量(和增长)大小的方形表的最佳方法是什么?或者没有好的解决方案,我的(或其他一些)解决方法更好?
答案 0 :(得分:5)
编辑注意:正如评论中所提到的,一旦你获得了足够的位置,只需存储长/纬度值并根据这些值来计算动态距离可能更有意义。但是,此处说明的解决方案仍可能与其他应用程序相关。
处理此问题的最佳方法是使用数据透视表,其中每行有两个地方ID和一个距离值。
现在,由于距离A-B与B-A相同,我们只需要存储一次配对。如果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字段(尽管有些人可能认为它有时仍然很好)。您将按如下方式设置一个唯一键(您将要查看documentation以了解正确用法):
UNIQUE KEY `UNIQUE_placesDistances_primary`(`place_id_1`,`place_id_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
中实际定义的地点添加条目。它还意味着(如果您使用默认的外键行为),如果您有距离行引用该地点,则无法删除该地点。
查看两个地方之间的距离
(给出两个变量@id_1
作为第一名的id,@id_2
作为第二名的id)
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;
我们使用OR来说明我们尝试查找距离2
到1
而不是1
到2
的情况 - 请记住,我们只存储值其中第一个地方的id小于第二个地方,以避免存储重复项。
插入新距离
(给出三个变量@id_1
作为第一个地方的ID,@id_2
作为第二个地方的ID,@distance
是距离)
INSERT `places_distances`(`place_id_1`,`place_id_2`,`distance`)
VALUES(LEAST(@id_1, @id_2),GREATEST(@id_1, @id_2), @distance)
我们正在使用内置的comparison functions LEAST
和GREATEST
来帮助维护我们的规则,即我们只存储第一个ID小于第二个ID的地方,以避免重复。
显示地名列表,按其从最远到最近的距离排序
要使places
表格中的原始名称显示在我们的places_distances
查询中,我们必须将它们连接在一起。在这种情况下,LEFT JOIN
是最佳选择,因为我们只关心places_distances
表中的内容。 For more info on MySQL joins check here
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
答案 1 :(得分:3)
我会为所有地方存储lat / long并编写一个函数来计算它们之间的距离和lat / long信息。
通过这种方式,您无需计算要在DB中添加的新地点的距离。
此外,如果你有很多地方,使用数据透视表只存储距离,你必须知道这个表可以非常快速地增长。因为你需要覆盖所有地方的组合。
例如:对于1000个位置,您的表中将有1000 * 1000 - 1000 = 999000行。为更大的数字做数学但是这个表可能包含很多行取决于你有多少个地方。
答案 2 :(得分:3)
将其分成另一个名为“距离”的表格,该表格与原始的“地点”表格相关:
创建表距离(place_id_1 int,place_id_2 int,distance int);
也就是说,对于每个地方,计算另一个地方的距离并将其保存在这个新表中。
答案 3 :(得分:2)
您可以创建一个新表,其中两列作为位置的外键,一列用于它们之间的距离。
|place1 | place2 | distance
-+-------|--------|---------
|.... |..... | .....
取决于你拥有多少个地点,这张桌子可以快速增长。
答案 4 :(得分:0)
最简单的方法是制作另一个包含两个地方id和
的距离的表place1 place2 distance
a b 20
c d 30
在获取数据时,只需将其与位置表连接即可。
答案 5 :(得分:-1)
我认为这样的事情可以胜任。
ORIGIN | CITY 1 | CITY 2 | CITY 3 | CITY 4 | CITY 5
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
CITY 1 0 20 40 20
CITY 5 10 50 20 0
CITY 3 10 0 10 40
您可以轻松获取到其他地方的距离,而且您不需要为您知道的每个距离存储城市的名称。
SELECT 'CITY 2' FROM DISTANCES WHERE ORIGIN='CITY 5'