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 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?