我有一张表,其中包含世界上所有地理位置及其关系的位置。
这是一个显示层次结构的示例。你会看到数据实际上存储为所有三个
数据显然也不会改变。下面是英格兰布莱顿位置的直系祖先示例,其 woeid 为 13911。
表:
geoplanet_places
(有 560 万行)
大图:http://chrisacky.com/ancestors.jpg
然后我有另一张桌子叫
entities
。该表存储我想映射到地理位置的项目。我存储了一些基本信息,但最重要的是我存储了woeid
,它是来自geoplanet_places
的外键。
最终
entities
表将包含数千个实体。我想要一种方法能够返回包含实体的所有节点的完整树。
我计划创建一些东西来促进根据实体的地理位置过滤和搜索实体,并能够发现在该特定节点上可以找到多少实体。
所以如果我的
entities
表中只有一个实体,我可能会有这样的东西
`地球(一)
英国 (1)
英格兰 (1)
东萨塞克斯 (1)
布莱顿和霍夫城 (1)
布莱顿 (1)`
然后假设我有另一个位于德文郡的实体,那么它会显示如下内容:
地球 (2)
英国 (2)
英格兰 (2)
德文 (1)
东萨塞克斯 (1) ...等
表示每个地理位置“内部”有多少实体的(计数)不需要是实时的。我可以忍受每小时生成我的对象并缓存它。
目标是能够创建一个界面,该界面可能开始仅显示具有实体的国家..
好喜欢
Argentina (1021)
, Chile (291)
, ...
, United States (32,103)
, United Kingdom (12,338)
然后用户将单击一个位置,例如英国,然后将获得所有直接子节点,这些子节点是英国的后代,并且其中有一个实体。
如果英国有 32 个县,但当您向下钻取时最终只有 23 个县存储了实体,那么我不想显示其他 9 个。它只是位置。
这个站点恰当地展示了我希望实现的功能: http://www.homeaway.com/vacation-rentals/europe/r5
你建议我如何管理这样的数据结构?
我正在使用的东西。
我计划尽可能快地进行钻取。我想创建一个搜索无缝的 AJAX 界面。
我也有兴趣知道您会推荐在哪些列上建立索引。
通常,层次结构中有三种查询会引起麻烦:
这是一张小表,显示了
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
表示答案取决于您是否将方法与邻接表混合,即。 e.将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
这是我提出的查询。这是对你的建议 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"
这将返回 Brighton 的所有父母。
您的查询的问题是它没有将路径返回给父母,而是返回共享相同路径的任何节点。
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