2011-01-28 60 views
6

我有一个表格,其中包含世界上所有地理位置的位置及其关系。我应该使用哪种分层模型?邻接,嵌套或枚举?

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

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

的数据显然是永远不会改变无论是。下面是一个具有13911.

表一WOEID在英国布赖顿的位置直接祖先的例子:geoplanet_places(已5.6million行) Ancestors 大图:http://tinyurl.com/68q4ndx

我再呼吁另一个表entities。此表存储我想要映射到地理位置的项目。我存储了一些基本信息,但最重要的是我存储woeid这是geoplanet_places的外键。 enter image description here

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

我打算创建一些内容,以便根据地理位置过滤和搜索实体,并能够发现在该特定节点上可以找到多少实体。

所以,如果我只有一个实体在我entities表,我可能有这样的事情

'地球(1)

英国(1)

英格兰(1)

东萨塞克斯郡(1)

布赖顿市(1)

布莱顿(1)`

让我们再有人说我这是位于德文郡的另一个实体,那么它会显示类似:

地球(2)

美国Kingom(2)

英格兰(2)

德文(1)

东萨西克斯郡(1) ...等等

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

的目的,就是为了能够创造可能开始时只显示其有实体的国家的接口..

所以像

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界面,这对于搜索来说是无可挑剔的。

我也很想知道你会建议索引哪些列。

+0

这是一个很好的问题! – 2012-01-25 21:31:39

回答

8

通常情况下,有三种类型的查询中,肇事层次:

  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 

childrenpoor/excellent意味着,答案取决于你与邻接表混合的方法,我。即将parentID存储在每条记录中。

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

  1. 所有祖先展现地球/英国/德文事情
  2. 所有的孩子,以示“在欧洲的目的地”(项目)
  3. 所有的后代显示“在欧洲的目的地”(计数)

我会去物化路径,因为这种层次很少改变(只有在战争,反抗等情况下)。

创建varchar列名为path,指数,并通过这样的值填充:

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 
+0

你会如何处理这样一个问题。正如你所看到的,我确实有parentID和lft rgt值。我不确定我是否从完全错误的角度看问题。也许我需要退后一步。例如,我只想返回任何一个节点的直接子节点和(Count)。但要获得此计数值,我仍然必须创建一个困难的查询。问题在于计数值在查询中计算并且不会被保留。如果我保存计数值,那么我也可以在我的查询中使用它。我只是困惑很多。 :) – Layke 2011-01-28 17:49:42

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