我有一张表格,其中包含世界上所有地理位置的位置及其关系。
以下是显示层次结构的示例。您将看到数据实际存储为全部三个
数据显然也从未改变过。下面是英格兰布莱顿位置的直接祖先的例子,其中有一个13911的死亡。
表:geoplanet_places
(有560万行)
大图:http://tinyurl.com/68q4ndx
然后我有另一个名为entities
的表。此表存储我想要映射到地理位置的项目。我存储了一些基本信息,但最重要的是我存储woeid
来自geoplanet_places
的外键。
最终entities
表将包含数千个实体。我想要一种能够返回包含实体的所有节点的完整树的方法。
我计划创建一些东西,以便根据实体的地理位置过滤和搜索实体,并能够发现在该特定节点上可以找到多少个实体。
因此,如果我的entities
表中只有一个实体,我可能会有类似的内容
`地球(1)
英国(1)
英格兰(1)
东萨塞克斯(1)
布莱顿和霍夫城(1)
布莱顿(1)`
然后让我说我有另一个位于德文郡的实体,那么它会显示如下:
地球(2)
United Kingom(2)
英格兰(2)
德文(1)
东萨塞克斯郡(1) ......等等。
将说明每个地理位置“内部”有多少实体的(计数)不需要是实时的。我可以忍受每小时生成我的对象并缓存它。
目标是能够创建一个界面,该界面可能只显示有实体的国家..
所以喜欢
Argentina (1021)
,Chile (291)
,...
,United States (32,103)
,United Kingdom (12,338)
然后,用户将点击某个位置,例如United Kindom,然后将获得所有直接子节点,这些子节点是英国的后代并且其中包含实体。
如果United Kindgdom中有32个县,但是当你向下钻取时只有23个县中存有实体,那么我不想显示其他9.这只是位置。
该网站恰当地展示了我希望实现的功能: http://www.homeaway.com/vacation-rentals/europe/r5
您如何建议我管理这样的数据结构?
我正在使用的东西。
我计划尽可能快地完成演练。我想创建一个无缝的AJAX界面进行搜索。
我也有兴趣知道您建议索引哪些列。
答案 0 :(得分:9)
通常,层次结构中有三种查询会导致麻烦:
这是一个小表,显示MySQL
中不同方法的表现:
Ancestors Descendants Children Maintainability InnoDB
Adjacency list Good Decent Excellent Easy Yes
Nested sets (classic) Poor Excellent Poor/Excellent Very hard Yes
Nested sets (spatial) Excellent Very good Poor/Excellent Very hard No
Materialized path Excellent Very good Poor/Excellent Hard Yes
在children
中,poor/excellent
表示答案取决于您是将方法与邻接列表混合,i。即在每条记录中存储parentID
。
对于您的任务,您需要所有三个查询:
我会选择物化路径,因为这种层次结构很少发生变化(仅在战争,反抗等情况下)。
创建一个名为path
的varchar列,对其进行索引并使用如下值填充它:
1:234:6345:45454:
其中数字是相应父母的主键,顺序正确(欧洲为1
,英国为234
等。)
您还需要一个名为levels
的表来保存从1
到20
的数字(或您想要的任何最大嵌套级别)。
选择所有祖先:
SELECT pa.*
FROM places p
JOIN levels l
ON SUBSTRING_INDEX(p.path, ':', l.level) <> p.path
JOIN places pa
ON pa.path = CONCAT(SUBSTRING_INDEX(p.path, ':', l.level), ':')
WHERE p.id = @id_of_place_in_devon
选择所有儿童及其中的地点数量:
SELECT pc.*, COUNT(pp.id)
FROM places p
JOIN places pc
ON pc.parentId = p.id
JOIN places pp
ON pp.path BETWEEN pc.path AND CONCAT(pc.path, ':')
AND pp.id NOT IN
(
SELECT parentId
FROM places
)
WHERE p.id = @id_of_europe
GROUP BY
pc.id
答案 1 :(得分:0)
这是我提出的查询。这是你对Quassnoi建议的改编。
SELECT pa.*, level, SUBSTRING_INDEX(p.ancestry, '/', l.level), p.*
FROM geoplanet_places p
JOIN levels l
ON SUBSTRING_INDEX(p.ancestry, '/', l.level) <> p.ancestry
JOIN geoplanet_places pa
ON pa.woeid = SUBSTRING_INDEX( SUBSTRING_INDEX(p.ancestry, '/', l.level),'/',-1)
WHERE p.woeid = "13911"
这将返回布莱顿的所有父母。
您的查询的问题是它没有返回父项的路径,而是返回共享相同路径的任何节点。
SELECT pa.*, GROUP_CONCAT(pa.name ORDER BY pa.lft asc),group_concat( pa.lft ), pa.ancestry
FROM geo_places p
JOIN levels l
ON SUBSTRING_INDEX(CONCAT(p.ancestry, p.woeid,'/'), '/', l.level) <> p.ancestry
JOIN geo_places pa
ON pa.woeid = SUBSTRING_INDEX( SUBSTRING_INDEX(CONCAT(p.ancestry, p.woeid,'/'), '/', l.level),'/',-1)
WHERE p.woeid IN ("12767488","12832668","12844837","131390","131391","12846428","24534461")
GROUP BY p.woeid