codeigniter3分表及列表查询处理

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;
    }
}

至此,分表的自动产生及列表查询程序已经完成,目前仅支持按年月分表,使用者可自行扩展,希望对读者有所启发。

******************************只要思想不滑坡,办法总比困难多 ******************************
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值