Laravel 使用 union all 分表联合查询

Laravel tytrock ⋅ 于 2018-11-29 14:30:16 ⋅ 4470 阅读

有一个项目的数据库,里面的一个表每个月都有非常多的数据写入,从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

回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter