codeigniter3分表及列表查询处理
一个trait类文件
该文件在我的项目目录为:application/traits/Split/SplitTableTrait.php
<?php
trait SplitTableTrait
{
//是否分表,默认false,即不分表
public $isSplitTable = false;
//原表
public $originTable;
//表
public $endTable;
/**
* 后缀参数
* @var string
*/
protected $suffix = null;
/**
* 年月参数:202104
* @var string
*/
public $ym;
public function init(array $attributes = [], $suffix = null)
{
//默认原表
$this->originTable = $this->table;
//默认最终表
$this->endTable = $this->table;
$this->ym = date('Ym');
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
//初始化分表表名并创建
$this->setSuffix();
}
/**
* 设置表后缀, 如果设置分表后缀,可在service层调用生成自定义后缀表名,
* 但每次操作表之前都需要调用该方法以保证数据表的准确性
* @param $suffix
*/
public function setSuffix($suffix = null)
{
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
if ($this->suffix !== null) {
//$this->endTable = $this->getTable() . '_' . $suffix;
$this->endTable = $this->originTable . '_' . $this->suffix;
//最终表替换模型中声明的表作为分表使用的表
$this->table = $this->endTable;
}
//调用时,创建分表,格式为 table_{$suffix}
//未传自定义后缀情况下,,默认按年月分表格式为:b_log_202101
//无论使用时是否自定义分表名,都会创建默认的分表,除非关闭该调用
$this->createTable();
}
/**
* 创建分表,没有则创建,有则不处理
*/
protected function createTable()
{
//初始化分表,,按年月分表格式为:b_log_202101
if (!$this->db->table_exists($this->endTable)) {
$this->db->query("create table {$this->endTable} like {$this->originTable}");
}
}
/**
* 默认日期参数字段
* @var null
*/
protected $defaultDateField = null;
/**
* 开始时间
* @var
*/
protected $startTime;
/**
* 结束时间
* @var
*/
protected $endTime;
/**
* 默认查询天数
* @var int
*/
protected $defaultDays = 30;
/**
* 是否限制日期查询,0:否,1:是,默认限制
* @var int
*/
protected $isLimitDays = 1;
/**
* 不传开始截止时间时,是否只查本月数据,默认是
* @var int
*/
protected $isCurMonth = 1;
/**
* 处理时间参数处
* @param array $params
*/
protected function dealDateParam(array $params)
{
//默认只查当前年月的数据
$this->startTime = isset($params['start_time']) && $params['start_time']
? date('Y-m-d H:i:s', strtotime($params['start_time'])) : false;
$this->endTime = isset($params['end_time']) && $params['end_time']
? date('Y-m-d H:i:s', strtotime($params['end_time'])) : false;
if ($this->defaultDateField && isset($params[$this->defaultDateField]) && $params[$this->defaultDateField]) {
$this->startTime = date('Y-m-d H:i:s', strtotime($params[$this->defaultDateField]));
$this->endTime = date('Y-m-d H:i:s', strtotime('-1 day', strtotime($params[$this->defaultDateField])));
}
if (!$this->startTime && !$this->endTime) {
$this->endTime = date('Y-m-d H:i:s');
if ($this->isCurMonth) {
$this->startTime = date('Y-m').'-01 00:00:00';
} else {
//$this->startTime = date('Y-m-d H:i:s', strtotime("-{$this->defaultDays} day"));
$this->startTime = date('Y-m-d H:i:s', strtotime("-{$this->defaultDays} day", strtotime($this->endTime)));
}
} elseif(!$this->startTime && $this->endTime) {
$this->startTime = date('Y-m-d H:i:s', strtotime("-{$this->defaultDays} day", strtotime($this->endTime)));
} elseif ($this->startTime && !$this->endTime) {
$this->endTime = date('Y-m-d H:i:s', strtotime("+{$this->defaultDays} day", strtotime($this->startTime)));
}
//限制只能查30天的数据
if ($this->isLimitDays) {
//$this->defaultDays += 1;
$realEndTime = date('Ymd', strtotime("+{$this->defaultDays} day", strtotime($this->startTime)));
$exceptEndTime = date('Ymd', strtotime($this->endTime));
if ($realEndTime < $exceptEndTime) {
output_error('split_table_10001', "分表查询异常:仅允许查询时间范围{$this->defaultDays}天的数据");
}
}
}
/**
* 查询字段
* @var string $fields
*/
protected $fields = '*';
/**
* 查询条件
* @var string $where
*/
protected $where = ' ';
/**
* 排序字段
* @var
*/
protected $orderByField = null;
/**
* 排序类型,asc:正序,desc:倒序,默认倒序
* @var
*/
protected $orderBy = 'desc';
/**
* 是否分页,0否,1:是,默认分页
* @var int
*/
public $isPage = 1;
/**
* 执行union all对分表的最终扥分页查询
* @param array $params
* @return array
*/
protected function dealListByUnionAllQuery(array $params)
{
// 循环比较年月,添加每一张表的查询
$startMonth = date('Ym', strtotime($this->startTime));
$endMonth = date('Ym', strtotime($this->endTime));
$sqlAry = [];
for($i = $endMonth; $i >= $startMonth; $i--) {
//根据是否分表,确定查询真正表
if ($this->isSplitTable) {
$curTable = "{$this->originTable}_{$i}";
} else {
$curTable = $this->originTable;
}
//如果表不存在则跳过本次循环
if (!$this->db->table_exists($curTable)) {
continue;
}
//sql
if (date('Ym', strtotime($this->startTime)) == $i) {
//查询开始时间条件
$startTimeWhere = " {$this->defaultDateField} >= '{$this->startTime}' ";
$startwhere = $this->where ? " and {$startTimeWhere} " : " where {$startTimeWhere} ";
$sql = "(select {$this->fields} "." from {$curTable} "
." {$this->where} {$startwhere} order by {$this->orderByField} {$this->orderBy}) ";
} elseif (date('Ym', strtotime($this->endTime)) == $i) {
//查询结束之间条件
$endTimeWhere = " {$this->defaultDateField} <= '{$this->endTime}' ";
$endwhere = $this->where ? " and {$endTimeWhere} " : " where {$endTimeWhere} ";
$sql = "(select {$this->fields} "." from {$curTable} "
." {$this->where} {$endwhere} order by {$this->orderByField} {$this->orderBy}) ";
} else {
$sql = "(select {$this->fields} "." from {$curTable} "
." {$this->where} order by {$this->orderByField} {$this->orderBy}) ";
}
$sqlAry[] = $sql;
}
if (!$sqlAry) {
output_error('split_table_10002', "分表查询异常:该日期范围内没有分表子表");
}
$subSql = implode(' union all ', $sqlAry);
//执行union all查询
//获取查询数量
$endSql = "select count(*) as count "." from ({$subSql}) as t";
$totalCount = $this->db->query($endSql)->row_array()['count'];
//分页查询数据
$limitAry = deal_page_limit_offset($params, 1);
$querySql = "select {$this->fields} "." from ({$subSql}) as t "
." order by {$this->orderByField} {$this->orderBy} {$limitAry['limit_offset']}";
$list = $this->db->query($querySql)->result_array();
$endList = ['list' => $list];
$endList['total_count'] = $totalCount;
return deal_auto_page($limitAry, $endList);
}
}
引入类文件
在通用模型类中加载trait类,由于codeigniter3没有命名空间,使用原始方式引入:
类外部加上:
require_once APPPATH.'traits/Split/SplitTableTrait.php';
类内部加上:
use SplitTableTrait;
示例图:
使用
<?php
/**
* Created by PhpStorm.
* User: 15237
* Date: 2019/4/15
* Time: 18:01
*/
class P_user_log_Model extends PS_Model
{
//分表用:是否分表,默认false,即不分表
public $isSplitTable = true;
//分表用:原始表名
public $table = 'p_user_log';
public $_p_user_log_tb = 'p_user_log';
public function __construct()
{
parent::__construct();
//分表用:初始化分表处理
$this->init();
//分表用:设置最终表
$this->_p_user_log_tb = $this->endTable;
}
/**
* 分表查日志,范围30天
* @param array $params
* @return array
*/
public function listMod(array $params = [])
{
//是否查询当月
$this->isCurMonth = 0;
//处理时间参数
$this->defaultDateField = 'oper_date';
$this->dealDateParam($params);
//排序字段
$this->orderByField = 'oper_date';
//查询条件:用户id
$userId = $params['user_id'];
$this->where = " where user_id={$userId} ";
//查询字段
$this->fields = 'admin_log_id, user_id, oper_date, oper_ip, oper_type, content';
return $this->dealListByUnionAllQuery($params);
}
}
这里实施一个模型类,里面是分表查询方法,真正使用还需要控制器层(controller)、服务层(service)方法调用才能形成完整的调用,这里就不赘述了。
必要函数放到helper文件中
由于trait类中使用了一些函数,而这些函数放在helper文件中,所以这里也列一下:
/**
* 输出失败JSON
* @param int $errCode
* @param string $message
* @return string
*/
function output_error($errCode, $message,array $datas=array())
{
$data['code'] = $errCode;
$data['msg'] = $message;
$data['action'] = get_api_action();
if(empty($datas)){
$data['data'] = new stdClass();
}else{
$data['data'] = $datas;
}
if (!empty($_GET['callback'])) {
echo $_GET['callback'] . '(' . json_encode($data) . ')';
exit;
} else {
echo json_encode($data);
exit;
}
}
if(!function_exists('deal_page_limit_offset')){
/**
* 方法 deal_page_limit_offset,处理分页条件
* @param array $params 参数数组
* @param int $type 分页类型(1:原生SQL语句,2:构造器链式操作SQL)
* @return mixed
*/
function deal_page_limit_offset($params, $type=1){
$page = 1;
$limit = 10;
$offset = 0;
if(array_key_exists('page_size', $params) && array_key_exists('page', $params)){
$page = empty($params['page']) ? $page : $params['page'];
$limit = empty($params['page_size']) ? $limit : $params['page_size'];
}elseif(array_key_exists('page_size', $params) && !array_key_exists('page', $params)){
$limit = empty($params['page_size']) ? $limit : $params['page_size'];
}elseif(!array_key_exists('page_size', $params) && array_key_exists('page', $params)){
$page = empty($params['page']) ? $page : $params['page'];
}
$offset = ($page-1)*$limit;
if($type == 1){
$limit_offset = ' limit '.$offset.','.$limit;
}else if($type == 2){
$limit_offset = array('limit'=>$limit, 'offset'=>$offset);
}
$limit_offset_ary['page'] = $page;
$limit_offset_ary['cur_page'] = $page;
$limit_offset_ary['page_size'] = $limit;
$limit_offset_ary['limit_offset'] = $limit_offset;
return $limit_offset_ary;
}
}
if(!function_exists('deal_auto_page')){
/**
* 方法 deal_auto_page,处理自动分页并得到最终的结果
* @param array $limit_offset_ary 分页参数数组
* @param array 数据列表数组
* @return array $list 返回手动处理分页完成的结果
*/
function deal_auto_page($limit_offset_ary, $list){
$page_size = (int)$limit_offset_ary['page_size'] ?? PAGE_SIZE;
$list['total_page'] = ceil($list['total_count'] / $page_size);
$list['page'] = (int)$limit_offset_ary['page'];
$list['cur_page'] = (int)$limit_offset_ary['cur_page'];
$list['page_size'] = $page_size;
return $list;
}
}
至此,分表的自动产生及列表查询程序已经完成,目前仅支持按年月分表,使用者可自行扩展,希望对读者有所启发。
******************************只要思想不滑坡,办法总比困难多
******************************
|