我应该使用哪种分层模型?邻接,嵌套或枚举?

时间:2011-01-28 17:22:49

标签: mysql enumeration nested-sets adjacency-list

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

以下是显示层次结构的示例。您将看到数据实际存储为全部三个

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

数据显然也从未改变过。下面是英格兰布莱顿位置的直接祖先的例子,其中有一个13911的死亡。

表:geoplanet_places(有560万行) Ancestors 大图:http://tinyurl.com/68q4ndx

然后我有另一个名为entities的表。此表存储我想要映射到地理位置的项目。我存储了一些基本信息,但最重要的是我存储woeid来自geoplanet_places的外键。 enter image description here

最终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 enter image description here

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

我正在使用的东西。

  • PHP
  • MySQL的
  • Solr的

我计划尽可能快地完成演练。我想创建一个无缝的AJAX界面进行搜索。

我也有兴趣知道您建议索引哪些列。

2 个答案:

答案 0 :(得分:9)

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

  1. 归还所有祖先
  2. 返回所有后代
  3. 归还所有孩子(直系后代)。
  4. 这是一个小表,显示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

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

    1. 显示地球/英国/德文郡事物的所有祖先
    2. 所有孩子都要出示“欧洲目的地”(项目)
    3. 所有后代都表示“欧洲目的地”(计数)
    4. 我会选择物化路径,因为这种层次结构很少发生变化(仅在战争,反抗等情况下)。

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

      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
      

答案 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