Which Hierarchical model should I use? Adjacency, Nested, or Enumerated?

前端 未结 2 917
礼貌的吻别
礼貌的吻别 2021-02-01 10:31

I have a table which contains a location of all geographical locations in the world and their relationships.

Here is a example that shows the hierarchy. You will see tha

相关标签:
2条回答
  • 2021-02-01 11:19

    Typically, there are three kinds of queries in the hierarchies which cause troubles:

    1. Return all ancestors
    2. Return all descendants
    3. Return all children (immediate descendants).

    Here's a little table which shows the performance of different methods in 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 means that the answer depends on whether you are mixing the method with adjacency list, i. e. storing the parentID in each record.

    For your task, you need all three queries:

    1. All ancestors to show the Earth / UK / Devon thing
    2. All children to show "Destinations in Europe" (the items)
    3. All descendants to show "Destinations in Europe" (the counts)

    I would go for materialized paths, since this kind of hierarchy rarely changes (only in case of war, revolt etc).

    Create a varchar column called path, index it and fill it with the value like this:

    1:234:6345:45454:
    

    where the numbers are primary keys of the appropriate parents, in correct order (1 for Europe, 234 for UK etc.)

    You will also need a table called levels to keep numbers from 1 to 20 (or whatever maximum nesting level you want).

    To select all ancestors:

    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
    

    To select all children and counts of places within them:

    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 讨论(0)
  • 2021-02-01 11:25

    This is the query that I came up. It is an adaption of what you suggestion 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"
    

    This returns all of the parents of Brighton.

    The problem with your query was that it wasn't return the path to parents, but instead any node which shared the same path.

    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
    
    0 讨论(0)
提交回复
热议问题