Cakephp项目重构——查询优化

正在开发一个工程管理系统,项目层级上分为实体项目和单项工程,实体项目是按区域区分的,比如闵行区项目、静安区项目;一个实体项目有多个单项工程。其中每个单项工程都有很多控制节点,每个节点由不同的用户负责。节点之间有前后置关系,前置节点完成后,本节点成为用户的待办节点。

功能需求:用户可以查看自己负责的节点、已经完成的节点、待办节点,可以通过时间段、区域筛选节点。

查询功能的UI:

数据结构:

单项工程-project_engineerings

实体项目-entity_projects

区域-material_areas

单项工程节点-project_engineering_nodes

用户-users

用户待办节点-users_project_engineering_nodes

查询页面要根据单项工程进行分页展示:

优化前的性能:

memory占用18MB,耗时4.03s,SQL占用2371ms

优化后:

memory占用15.63MB,耗时0.22秒,SQL耗时103ms

可以看到效果还是比较显著的。下面来看看完整优化的代码:

public function myPreNodeIndex($quickSearch = '')
    {
        $projectEngineeringNodeMatchCondition = [];//查询中关联单项工程节点的过滤条件
        $usersProjectEngineeringNodesMatchCondition = [];//查询中关联待办节点的过滤条件
        $projectEngineeringsCondition = [];//查询中单项工程的过滤条件

//        $this->request->session()->destroy();
        $userId = $this->request->session()->read('tcm_userId');
        $usersProjectEngineeringNodesMatchCondition = ['UsersProjectEngineeringNodes.user_id' => $userId];//默认查询用户待办节点

        //->搜索框的提交
        if ($this->request->is('post')) {
            $usersProjectEngineeringNodesMatchCondition = [];

            $this->request->session()->delete('projectEngineeringNodes.myPreNodeIndex.projectEngineeringNodeMatchCondition');
            $this->request->session()->delete('projectEngineeringNodes.myPreNodeIndex.usersProjectEngineeringNodesMatchCondition');
            $this->request->session()->delete('projectEngineeringNodes.myPreNodeIndex.projectEngineeringsCondition');

            $projectName = $_POST['project_name'];//单项工程名称
            $code = $_POST['code'];//单项工程编号
            $materialAreaId = $_POST['material_area_id'];//区域
            $nodePlanStart = $_POST['node_plan_start'];//节点计划开始时间
            $nodePlanEnd = $_POST['node_plan_end'];//节点计划结束时间
            $nodeState = $_POST['node_state'];//节点状态
            if ($projectName) {//单项工程名称
                $projectEngineeringsCondition[] = ['ProjectEngineerings.name LIKE ' => "%{$projectName}%"];
            }
            if ($code) {//单项工程编号
                $projectEngineeringsCondition[] = ['ProjectEngineerings.code LIKE ' => "%{$code}%"];
            }
            if ($materialAreaId) {//区域
                $projectEngineeringsCondition[] = ['EntityProjects.material_area_id' => $materialAreaId];
            }

            //查询节点开始/结束时间/节点状态
            $this->request->session()->delete('schMemery.projectEngineeringIds');
            if ($nodePlanStart) {//节点计划开始时间
                $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.start_date >=' => $nodePlanStart];
            }
            if ($nodePlanEnd) {//节点计划结束时间
                $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.end_date <' => $nodePlanEnd];
            }

            if ($nodeState) {//节点状态
                switch ($nodeState) {
                    case 1://待办
                        //获得用户包含待办节点的单项工程
                        $usersProjectEngineeringNodesMatchCondition[] = ['UsersProjectEngineeringNodes.user_id' => $userId];
                        break;
                    case 2://已完成
                        $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.is_complete' => 1];
                        break;
                    case 3://未完成
                        $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.is_complete' => 0];
                        break;
                    default:
                        $usersProjectEngineeringNodesMatchCondition = [];
                        break;
                }
            }

            // 把搜索条件写入session
            $schMemery = [
                'projectName' => $projectName, 'projectCode' => $code, 'materialAreaId' => $materialAreaId, 'nodePlanStart' => $nodePlanStart, 'nodePlanEnd' => $nodePlanEnd, 'nodeState' => $nodeState
            ];
            $this->request->session()->write('schMemery.ProjectEngineeringNodes-myPreNodeIndex', $schMemery);
        }
        //是否有缓存的查询条件
        if($this->request->session()->check('schMemery.ProjectEngineeringNodes-myPreNodeIndex'))
            $realSchMem = $this->request->session()->read('schMemery.ProjectEngineeringNodes-myPreNodeIndex');
        //<-搜索框的提交

        //快捷查询
        if ($quickSearch) {
            //本月第一天与最后一天
            $thisMonthBeginDate = date('Y-m-01', strtotime(date('Y-m-d')));
            $thisMonthEndDate = date('Y-m-d', strtotime("$thisMonthBeginDate +1 month"));

            switch ($quickSearch) {
                case 'todoPerMonth'://我的本月待办节点
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.start_date >=' => $thisMonthBeginDate];//开始时间
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.end_date <' => $thisMonthEndDate];//结束时间
                    $usersProjectEngineeringNodesMatchCondition[] = ['UsersProjectEngineeringNodes.user_id' => $userId];//待办
                    break;
                case 'completePerMonth'://我的本月已完成节点
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.start_date >=' => $thisMonthBeginDate];//开始时间
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.end_date <' => $thisMonthEndDate];//结束时间
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.is_complete' => 1];//已完成节点
                    break;
                case 'allPerMonth'://我的本月未完成节点
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.start_date >=' => $thisMonthBeginDate];//开始时间
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.end_date <' => $thisMonthEndDate];//结束时间
                    $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.is_complete' => 0];//未完成节点
                    break;
                default:
                    break;
            }
        }

        //将查询写入session, 保持分页
        if ($projectEngineeringNodeMatchCondition)
            $this->request->session()->write('projectEngineeringNodes.myPreNodeIndex.projectEngineeringNodeMatchCondition', $projectEngineeringNodeMatchCondition);
        if ($usersProjectEngineeringNodesMatchCondition)
            $this->request->session()->write('projectEngineeringNodes.myPreNodeIndex.usersProjectEngineeringNodesMatchCondition', $usersProjectEngineeringNodesMatchCondition);
        if ($projectEngineeringsCondition)
            $this->request->session()->write('projectEngineeringNodes.myPreNodeIndex.projectEngineeringsCondition', $projectEngineeringsCondition);

        //->构造查询:结合了搜索结果及分页
        if ($this->request->session()->check('projectEngineeringNodes.myPreNodeIndex.projectEngineeringNodeMatchCondition'))//查询缓存
            $projectEngineeringNodeMatchCondition = $this->request->session()->read('projectEngineeringNodes.myPreNodeIndex.projectEngineeringNodeMatchCondition');
        if ($this->request->session()->check('projectEngineeringNodes.myPreNodeIndex.usersProjectEngineeringNodesMatchCondition'))//查询缓存
            $usersProjectEngineeringNodesMatchCondition = $this->request->session()->read('projectEngineeringNodes.myPreNodeIndex.usersProjectEngineeringNodesMatchCondition');
        if ($this->request->session()->check('projectEngineeringNodes.myPreNodeIndex.projectEngineeringsCondition'))//查询缓存
            $projectEngineeringsCondition = $this->request->session()->read('projectEngineeringNodes.myPreNodeIndex.projectEngineeringsCondition');

        $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.user_id' => $userId];//用户负责节点
        $projectEngineeringNodeMatchCondition[] = ['ProjectEngineeringNodes.single_project_id !=' => 0];//节点包含单项工程ID
        $projectEngineeringsCondition[] = ['ProjectEngineerings.problem_flag' => 0];//单项工程没有标记有问题
        $projectEngineeringQuery = $this->ProjectEngineerings
            ->find()
            ->contain(['EntityProjects.MaterialAreas', 'ProjectEngineeringNodes', 'ProjectEngineeringNodes.UsersProjectEngineeringNodes'])
            ->select(['id', 'name', 'code', 'area_name' => 'MaterialAreas.name'])
            ->where($projectEngineeringsCondition)//单项工程没有标记有问题
            ->matching('ProjectEngineeringNodes', function ($q) use ($projectEngineeringNodeMatchCondition) {//节点过滤
                return $q->where($projectEngineeringNodeMatchCondition);
            });

        //待办
        if ($usersProjectEngineeringNodesMatchCondition) {
            $projectEngineeringQuery->matching('ProjectEngineeringNodes.UsersProjectEngineeringNodes', function ($q) use ($usersProjectEngineeringNodesMatchCondition) {//节点过滤
                return $q->where($usersProjectEngineeringNodesMatchCondition);
            });
        }

        // 设置分页页数
        $this->paginate = [
            'limit' => 10,
            'order' => ['ProjectEngineerings.created' => 'desc'],
        ];
        $projectEngineerings = $this->paginate($projectEngineeringQuery);
        //<-构造查询:结合了搜索结果及分页

        //构造显示数据, 节点按类型分组
        $projectEngineeringsView = [];
        foreach ($projectEngineerings as $key => $val) {
            $userNodes = [];//用户节点
            $projectNodes = [];//项目节点
            // 找到所有的节点类型
            $nodeTypes = $this->ProjectNodeTypes->find('list')->toArray();
            foreach ($val['project_engineering_nodes'] as $eachNode) {
                foreach ($nodeTypes as $nodeTypeId => $eachNodeTypeName) {
                    if ($eachNode->project_node_type_id == $nodeTypeId) {
                        $projectNodes[$nodeTypeId][] = $eachNode;
                        if ($eachNode->user_id == $userId) {
                            $userNodes[$nodeTypeId][] = $eachNode;
                        }
                    }
                }
            }

            $projectEngineeringsView[$key][] = $userNodes;
            $projectEngineeringsView[$key][] = $projectNodes;
        }

        //区域
        $materialAreas = $this->loadModel('MaterialAreas')->find('list');
        //节点类型
        $projectNodeTypes = $this->loadModel('ProjectNodeTypes')->find('list')->toArray();

        $this->set(compact('materialAreas', 'realSchMem', 'tmpQuery', 'projectEngineerings', 'userId', 'projectNodeTypes', 'projectEngineeringsView'));
        $this->viewBuilder()->layout('sanse');
    }

主要是将所有的查询构造了一个查询语句:

$projectEngineeringQuery = $this->ProjectEngineerings
            ->find()
            ->contain(['EntityProjects.MaterialAreas', 'ProjectEngineeringNodes', 'ProjectEngineeringNodes.UsersProjectEngineeringNodes'])
            ->select(['id', 'name', 'code', 'area_name' => 'MaterialAreas.name'])
            ->where($projectEngineeringsCondition)//单项工程没有标记有问题
            ->matching('ProjectEngineeringNodes', function ($q) use ($projectEngineeringNodeMatchCondition) {//节点过滤
                return $q->where($projectEngineeringNodeMatchCondition);
            });

最终的SQL:

SELECT 
  ProjectEngineerings.id AS `ProjectEngineerings__id`, 
  ProjectEngineerings.name AS `ProjectEngineerings__name`, 
  ProjectEngineerings.code AS `ProjectEngineerings__code`, 
  MaterialAreas.name AS `area_name` 
FROM 
  project_engineerings ProjectEngineerings 
  INNER JOIN project_engineerings_nodes ProjectEngineeringNodes ON (
    ProjectEngineeringNodes.start_date >= '2018-01-03' 
    AND ProjectEngineeringNodes.end_date < '2018-11-09' 
    AND ProjectEngineeringNodes.user_id = 125 
    AND ProjectEngineeringNodes.single_project_id != 0 
    AND ProjectEngineerings.id = (
      ProjectEngineeringNodes.project_engineerings_id
    )
  ) 
  INNER JOIN users_project_engineering_nodes UsersProjectEngineeringNodes ON (
    UsersProjectEngineeringNodes.user_id = 125 
    AND ProjectEngineeringNodes.id = (
      UsersProjectEngineeringNodes.project_engineer_node_id
    )
  ) 
  LEFT JOIN entity_projects EntityProjects ON EntityProjects.id = (
    ProjectEngineerings.entity_project_id
  ) 
  LEFT JOIN material_areas MaterialAreas ON MaterialAreas.id = (
    EntityProjects.material_area_id
  ) 
WHERE 
  ProjectEngineerings.problem_flag = '0' 
ORDER BY 
  ProjectEngineerings.created desc 
LIMIT 
  10 OFFSET 0

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭记北宸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值