dongwen7380 2017-02-28 13:45
浏览 120
已采纳

递归函数对于创建分层选择列表来说太慢了

I have a query that gets me a Geo-Structure from the database.

Categories Table (30.000 rows inside):

id  title          parent  type 
-------------------------------
1   germany             0     1
2   bavaria             1     2
3   upper bavaria       2     3
4   munich              3     4
6   italy               0     1
7   toscana             6     2
8   city florence       7     3
9   florence            8     4

Categories Language Table

cid language title
--------------------------
1   en-UK    germany
2   de-DE    deutschland

Objects table:

id  title   landid  regionid    uregionid   cityid
--------------------------------------------------
1   o1           1         2            3        4
2   o2           1         2            3        4 
3   o3           6         7            8        9

MySQL query:

SELECT c.id, c.title, l.title AS translated, c.type, c.parent, count(c.id) as cnt
FROM category c
LEFT JOIN objects o ON (o.landid = c.id  OR o.regionid = c.id OR o.uregionid = c.id OR o.cityid = c.id) 
LEFT JOIN category_lang l ON l.cid = c.id AND l.language = "en-UK"
WHERE c.published = 1 AND o.published = 1
GROUP BY c.id
ORDER BY c.parent

I get an associative array ($tree) with values like here:

Array
(
    [0] => Array
        (
            [id] => 1
            [title] => Germany
            [type] => 1
            [parent] => 0
            [cnt] => 1
        )

    [1] => Array
        (
            [id] => 6
            [title] => Italy
            [type] => 1
            [parent] => 0
            [cnt] => 1
        )

    [2] => Array
        (
            [id] => 2
            [title] => Bavaria
            [type] => 2
            [parent] => 1
            [cnt] => 1
        )

    [3] => Array
        (
            [id] => 7
            [title] => Toscana
            [type] => 2
            [parent] => 6
            [cnt] => 1
        )

    [4] => Array
        (
            [id] => 3
            [title] => Upper Bavaria
            [type] => 3
            [parent] => 2
            [cnt] => 1
        )

    [5] => Array
        (
            [id] => 8
            [title] => City Florence
            [type] => 3
            [parent] => 7
            [cnt] => 1
        )

    [6] => Array
        (
            [id] => 4
            [title] => Munich
            [type] => 4
            [parent] => 3
            [cnt] => 1
        )

    [7] => Array
        (
            [id] => 9
            [title] => Florence
            [type] => 4
            [parent] => 8
            [cnt] => 1
        )

)

Then i create a structure that will prepare the display of a select list:

public static function buildTree($tree, $root = 0) { 
        $return = array(); 
        foreach($tree as $child) { 
            if($child['parent'] == $root) { 
                $return[] = array( 
                    'name' => $child, 
                    'next' => self::buildTree($tree, $child['id']) 
                ); 
            } 
        } 
        return empty($return) ? null : $return;     
    } 

Then i send the structure inside $return to a function to create the final select list:

public static function buildSelect($tree, $s) {
        $option = '';
        if(!is_null($tree) && count($tree) > 0) {
            foreach($tree as $node) {
                $selected = '';
                $class_type = $node['name']['type'];                
                $option .= '<option value="'.$node['name']['id'].'" class="h'.$class_type.'" '.$selected.' data-type="'.$node['name']['type'].'">'
                            .$node['name']['title']. ' (' . $node['name']['cnt'] . ')</option>'
                            . self::buildSelect($node['next'], $s); 
            }
            return $option; 
        }    
    } 

This all works but if the Geo-Structure gets really big the db query gets terrible slow. Would appreciate any ideas about how to speed this up, thanks!

  • 写回答

2条回答 默认 最新

  • dongyan7851 2017-02-28 16:53
    关注

    The basics seem there, and without setting up a lot of test data it is difficult to do any testing.

    The following has a couple of minor tweaks (you will need to change to cope with where you want to keep the compare routine). Possibly not enough to fix the issue. However I would be interested to know what effect it has, and also some timings of which method is taking the time, and how it ramps up:-

    <?php
    
    function cmp($a, $b)
    {
        return strcmp($a["parent"], $b["parent"]);
    }
    
    usort($tree, "cmp");
    
    $recursive_tree = self::buildTree($tree);
    
    echo self::buildSelect($recursive_tree, '');
    
    public static function buildTree(&$tree, $root = 0) 
    { 
        $return = array(); 
        foreach($tree as $child) 
        { 
            if($child['parent'] == $root) 
            { 
                $return[] = array( 
                    'name' => $child, 
                    'next' => self::buildTree($tree, $child['id']) 
                ); 
            }
            else
            {
                if ($child['parent'] > $root)
                {
                    break;
                }
            }
        } 
        return empty($return) ? null : $return;     
    } 
    
    
    public static function buildSelect(&$tree, $s) 
    {
        $option = '';
        if(!is_null($tree) && count($tree) > 0) 
        {
            foreach($tree as $node) 
            {
                $selected = '';
                $class_type = $node['name']['type'];                
                $option .= '<option value="'.$node['name']['id'].'" class="h'.$class_type.'" '.$selected.' data-type="'.$node['name']['type'].'">'
                            .$node['name']['title']. ' (' . $node['name']['cnt'] . ')</option>'
                            . self::buildSelect($node['next'], $s); 
            }
            return $option; 
        }    
    }
    

    Does the output have to be built up, or can be be directly output (even to a temp file)?

    EDIT

    If your objects table has indexes on landid, regionid, uregionid and cityid (ie, separate indexes on each one) then try this:-

    SELECT c.id, c.title, c.type, c.parent, count(c.id) as cnt
    FROM category c
    LEFT JOIN objects o1 ON o1.landid = c.id AND o1.published = 1
    LEFT JOIN objects o2 ON o2.regionid = c.id AND o2.published = 1
    LEFT JOIN objects o3 ON o3.uregionid = c.id AND o3.published = 1
    LEFT JOIN objects o4 ON o4.cityid = c.id AND o4.published = 1
    WHERE c.published = 1 
    AND (01.id IS NOT NULL
    OR 02.id IS NOT NULL
    OR 03.id IS NOT NULL
    OR 04.id IS NOT NULL)
    GROUP BY c.id, 
            c.title, 
            c.type, 
            c.parent
    ORDER BY c.parent
    

    EDIT

    Adding in your language table:-

    SELECT c.id,
            c.title, 
            l.title AS translated, 
            c.type, 
            c.parent, 
            count(c.id) as cnt
    FROM category c
    LEFT OUTER JOIN category_lang l ON l.cid = c.id AND l.language = "en-UK"
    LEFT OUTER JOIN objects o1 ON o1.landid = c.id AND o1.published = 1
    LEFT OUTER JOIN objects o2 ON o2.regionid = c.id AND o2.published = 1
    LEFT OUTER JOIN objects o3 ON o3.uregionid = c.id AND o3.published = 1
    LEFT OUTER JOIN objects o4 ON o4.cityid = c.id AND o4.published = 1
    WHERE c.published = 1 
    AND (01.id IS NOT NULL
    OR 02.id IS NOT NULL
    OR 03.id IS NOT NULL
    OR 04.id IS NOT NULL)
    GROUP BY c.id, 
            c.title, 
            translated,
            c.type, 
            c.parent
    ORDER BY c.parent
    

    As to whether this or the UNION solution is better, that will come down largely to your data. For example this solution will struggle if the 4 id columns in the objects table can have duplicates (unlikely, as I would doubt a city can also be a region).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化