我应该使用哪个层次模型?邻接、嵌套还是枚举?

问题描述 投票:0回答:2

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

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

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

数据显然也不会改变。下面是英格兰布莱顿位置的直系祖先示例,其 woeid 为 13911。

表:

geoplanet_places
(有 560 万行) Ancestors 大图:http://chrisacky.com/ancestors.jpg

然后我有另一张桌子叫

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

最终

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

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

我正在使用的东西。

  • PHP
  • MySQL
  • Solr

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

我也有兴趣知道您会推荐在哪些列上建立索引。

mysql enumeration nested-sets adjacency-list
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

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

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

  1. 所有祖先展示地球/英国/德文郡的东西
  2. 所有孩子展示“欧洲的目的地”(物品)
  3. 所有后代展示“欧洲的目的地”(伯爵)

我会选择物质化的道路,因为这种等级制度很少改变(仅在战争、叛乱等情况下)。

创建一个名为

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

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"

这将返回 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
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.