有一个项目的数据库,里面的一个表每个月都有非常多的数据写入,从2013年到现在(2018年)已经储存了近千万条数据,导致查询速度耗时长,后来通过改造,将这个表以年为单位分开了多个表储存,如2013年的数据储存到mydata_2013表,2014年的数据储存到mydata_2014表,以此类推,每个表的字段相同。这样当查询某年数据的时候速度确实是快了很多,但又衍生了另一个问题,无法运用以前的常规查询对所有数据进行关键字搜索。
后来通过搜索相关资料,才知道Laravel的Eloquent有强大的关联,访问器修改,查询范围等等这些功能让代码非常简洁。
首先需要我们需要在model里封装一个union表的方法,让这个model自动把涉及的分表作为一张表赋予model查询:
public function setUnionAllTable($attributes = ['*'], $wheres = [],$start = 2013, $end = null) { $end = $end?$end:date('Y'); //约束条件 $whereConditions = []; //涉及的表数组 $tables = []; //循环where数组,格式是[['字段','表达式','值',' and|or '],['字段','表达式','值',' and|or ']] foreach ($wheres as $val) { //组装每个where条件 $whereConditions[] = " and {$val[0]} {$val[1]} {$val[2]}"; } //循环开始日期和结束日期计算跨越的表 for ($i = $start; $i <= $end; $i++) { $tables[] = 'select ' . implode(',', $attributes) . ' from mydata_' . $i . ' where 1' . implode('', $whereConditions); } //会得到每一个表的子查询,因为都有约束条件,所以每一个子查询得结果集都不会很多 //用setTable的方法把这个子查询union all 后 as一个表名作为model的table属性 //sql大概会是:(select xxx,xxx from mydata_2013 where 1 and xxx union all select xxx,xxx from mydata_2014 where 1 and xxx) as mydata //核心是看你输入的开始日期和结束日期和约束条件,组装成一个union all的子查询然后作为table赋予model return $this->setTable(DB::raw('(' . implode(' union all ', $tables) . ') as mydata')); }
控制器里使用:
$mydata = new Mydata; $lists = $mydata ->setUnionAllTable(['name', 'time'],[['name','like','"%阳光%"']])->select(DB::raw('name,time'))->orderBy('time','DESC')->paginate(20);
参考资料:https://segmentfault.com/a/1190000010622484