8

我有一张表,其中包含世界上所有地理位置的位置及其关系。

这是一个显示层次结构的示例。您将看到数据实际上存储为所有三个

  • 枚举路径
  • 邻接表
  • 嵌套集

数据显然也永远不会改变。以下是英格兰布莱顿地区的直系祖先的示例,其 woeid 为 13911。

表:(有 560 万行) 大图:httpgeoplanet_places : //tinyurl.com/68q4ndx 祖先

然后我有另一个名为entities. 此表存储我想映射到地理位置的项目。我存储了一些基本信息,但最重要的woeid是我存储了来自geoplanet_places. 在此处输入图像描述

最终该entities表将包含数千个实体。而且我想要一种能够返回包含实体的所有节点的完整树的方法。

我计划创建一些东西来促进基于地理位置的实体的过滤和搜索,并能够发现在该特定节点上可以找到多少实体。

所以如果我的表中只有一个实体entities,我可能会有这样的东西

`地球 (1)

英国 (1)

英格兰 (1)

东萨塞克斯 (1)

布莱顿霍夫城 (1)

布莱顿 (1)`

然后假设我有另一个位于德文郡的实体,然后它将显示如下内容:

地球 (2)

联合王国 (2)

英格兰 (2)

德文 (1)

东萨塞克斯 (1) ... 等

将说明每个地理位置“内部”有多少实体的(计数)不需要是活的。我可以忍受每小时生成我的对象并缓存它。

目的是能够创建一个界面,该界面可能一开始只显示具有实体的国家/地区。

所以喜欢

Argentina (1021), Chile (291), ..., United States (32,103),United Kingdom (12,338)

然后,用户将单击一个位置,例如 United Kingdom,然后将获得所有直接子节点,这些子节点是 United Kingdom 的后代,并且其中有一个实体。

如果英国有 32 个县,但最终只有 23 个县有实体存储在其中,那么我不想显示其他 9 个。它只是位置。

该站点恰当地展示了我希望实现的功能:http: //www.homeaway.com/vacation-rentals/europe/r5 在此处输入图像描述

你建议我如何管理这样的数据结构?

我正在使用的东西。

  • PHP
  • MySQL
  • 索尔

我计划让钻取尽可能快。我想创建一个 AJAX 界面,搜索时会无缝。

我也很想知道您建议在哪些列上建立索引。

4

2 回答 2

9

通常,层次结构中有三种查询会导致问题:

  1. 归还所有祖先
  2. 返回所有后代
  3. 返回所有孩子(直系后代)。

这是一个小表,显示了不同方法的性能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

In children,poor/excellent表示答案取决于您是否将该方法与邻接列表混合,即parentID在每条记录中存储 。

对于您的任务,您需要所有三个查询:

  1. 所有祖先展示地球/英国/德文郡的事情
  2. 所有儿童展示“欧洲目的地”(项目)
  3. 所有后代都显示“欧洲目的地”(计数)

我会选择物化路径,因为这种等级制度很少改变(仅在战争、起义等情况下)。

创建一个名为 varchar 的列path,对其进行索引并用如下值填充它:

1:234:6345:45454:

其中数字是适当父母的主键,顺序正确(1对于欧洲,234对于英国等)

您还需要一个调用表levels来保存数字从120(或任何您想要的最大嵌套级别)。

选择所有祖先:

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
于 2011-01-28T17:42:34.977 回答
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
于 2011-02-14T21:08:45.897 回答