编辑3:通过将flatten方法更改为仅合并数组(如果不为空),将其降低到300-500 ms。
编辑2:通过仅为非空数组调用array_replace
将其降低到1.6秒。现在剩下要做的就是优化函数sort_categories_and_sub_categories
。那就是现在的瓶颈。如果我删除,我会降到300毫秒。有什么想法吗?
get_all_categories_and_sub_categories
foreach(array_keys($categories) as $id)
{
$subcategories = $this->get_all_categories_and_sub_categories($id, $depth + 1);
if (!empty($subcategories))
{
$categories = array_replace($categories, $subcategories);
}
}
修改
通过在get_all方法中执行缓存,我将性能提高了50%以上(6秒 - > 2.5秒)。它将查询量从3000减少到1.我仍然想知道为什么它很慢。
我有以下方法来获取类别和嵌套子类别。如果用户有几百(或千)个顶级类别,则会为每个类别执行一系列查询以查找子级。在一个案例中,我有3000个类别,它做了3000个查询。有没有办法优化这个以减少查询?或者,我应该检查他们是否有很多类别,不要尝试显示嵌套。
function get_all_categories_and_sub_categories($parent_id = NULL, $depth = 0)
{
$categories = $this->get_all($parent_id);
if (!empty($categories))
{
foreach($categories as $id => $value)
{
$categories[$id]['depth'] = $depth;
}
foreach(array_keys($categories) as $id)
{
$categories = array_replace($categories, $this->get_all_categories_and_sub_categories($id, $depth + 1));
}
return $categories;
}
else
{
return $categories;
}
}
function get_all($parent_id = NULL, $limit=10000, $offset=0,$col='name',$order='asc')
{
static $cache = array();
if (!$cache)
{
$this->db->from('categories');
$this->db->where('deleted',0);
if (!$this->config->item('speed_up_search_queries'))
{
$this->db->order_by($col, $order);
}
$this->db->limit($limit);
$this->db->offset($offset);
foreach($this->db->get()->result_array() as $result)
{
$cache[$result['parent_id'] ? $result['parent_id'] : 0][] = array('name' => $result['name'], 'parent_id' => $result['parent_id'], 'id' => $result['id']);
}
}
$return = array();
$key = $parent_id == NULL ? 0 : $parent_id;
if (isset($cache[$key]))
{
foreach($cache[$key] as $row)
{
$return[$row['id']] = array('name' => $row['name'], 'parent_id' => $row['parent_id']);
}
return $return;
}
return $return;
}
function sort_categories_and_sub_categories($categories)
{
$objects = array();
// turn to array of objects to make sure our elements are passed by reference
foreach ($categories as $k => $v)
{
$node = new StdClass();
$node->id = $k;
$node->parent_id = $v['parent_id'];
$node->name = $v['name'];
$node->depth = $v['depth'];
$node->children = array();
$objects[$k] = $node;
}
// list dependencies parent -> children
foreach ($objects as $node)
{
$parent_id = $node->parent_id;
if ($parent_id !== null)
{
$objects[$parent_id]->children[] = $node;
}
}
// clean the object list to make kind of a tree (we keep only root elements)
$sorted = array_filter($objects, array('Category','_filter_to_root'));
// flatten recursively
$categories = self::_flatten($sorted);
$return = array();
foreach($categories as $category)
{
$return[$category->id] = array('depth' => $category->depth, 'name' => $category->name, 'parent_id' => $category->parent_id);
}
return $return;
}
static function _filter_to_root($node)
{
return $node->depth === 0;
}
static function _flatten($elements)
{
$result = array();
foreach ($elements as $element)
{
if (property_exists($element, 'children'))
{
$children = $element->children;
unset($element->children);
}
else
{
$children = null;
}
$result[] = $element;
if (isset($children))
{
$flatened = self::_flatten($children);
if (!empty($flatened))
{
$result = array_merge($result, $flatened);
}
}
}
return $result;
}