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 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000
  • ¥30 ppOCRLabel导出识别结果失败
  • ¥15 Centos7 / PETGEM
  • ¥15 csmar数据进行spss描述性统计分析
  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题