幸福

如人饮水,冷暖自知

Laravel输出查询原生SQL小改做异步导出任务

近日商城项目功能优化需求,因原来使用传统的php在线导出,当订单量过大时,容易执行超时,需要更改商城相关订单导出业务逻辑,即将在线即时导出改为异步导出,具体实施方案是前端提交导出申请,后台使用python脚本执行导出任务并上传七牛,生成报表下载地址。

通过分析,发现原来报表导出SQL不存在慢查询问题,仅仅是导出量大时执行超时的问题,所以新建了一张导出任务表,将所有导出申请存进去,问题来了,任务在后台执行的时候,怎么知道用户需要导出哪些内容,因为当下订单管理还涉及到许多搜索项,于是乎原先想到的就是将各个搜索条件存成json,然后任务执行的时候,把搜索条件再逐个去拼接,后来发现可行性不大,因为依据不同的搜索条件可能还要关联不同的业务表,瞬间就变复杂了。

这个时候突然想到了如果能把用户的所有导出申请、搜索条件等直接当一条查询SQL存进去岂不方便,这个时候就用到了Laravel打印SQL的方式:

DB::enableQueryLog();
$result['data'] = $query->orderBy($column, $sort)->get();
$sql = DB::getQueryLog();

输出结果:

Array
(
    [0] => Array
        (
            [query] => select `guider_order`.*, `guider`.`nickname`, `guider`.`mobile`, `guider`.`title` from `guider_order` left join `guider` on `guider`.`id` = `guider_order`.`guider_id` where `guider_order`.`shop_id` = ? and `guider_order`.`order_sn` like ? order by `guider_order`.`created_at` desc
            [bindings] => Array
                (
                    [0] => 1
                    [1] => %E2017020611031275494%
                )

            [time] => 8.82
        )
)

所以在设计的导出任务表里面就直接存进了“query”,以为完事了,最后在取出直接执行的时候脚本报错,才发现遇到坑了,因为这样打印的SQL搜索条件参数的等默认都是“?”替代的,无法直接运行,后来看到数组里面的“bindings”下标,这才发觉查询参数等信息字段都在这个数组下标里面,得通过拼接才能组合成完整的SQL,所以经过一番思忖,采用了如下方式:

DB::enableQueryLog();
$result['data'] = $query->orderBy($column, $sort)->get();
$sql = DB::getQueryLog();

$bindings = $sql[0]['bindings'];

echo '<pre>';
print_r($sql);

$sqlArr = explode('?', $sql[0]['query']);
$exportSql = '';
foreach ($sqlArr as $key => $val) {
    if(isset($bindings[$key]) && $bindings[$key]) {
        $joinStr = $bindings[$key];
        $exportSql .= $val."'".$joinStr."'";
    }else{
        $exportSql .= $val;
    }
}

print_r($exportSql);

最后$exportSql输出如下:

select `guider_order`.*, `guider`.`nickname`, `guider`.`mobile`, `guider`.`title` from `guider_order` left join `guider` on `guider`.`id` = `guider_order`.`guider_id` where `guider_order`.`shop_id` = '1' and `guider_order`.`order_sn` like '%E2017020611031275494%' order by `guider_order`.`created_at` desc

这就得到了想要的原生SQL,在做任务时直接从当前记录取出执行即可。

涉及到其它优化细则,比如python脚本多进程执行时,可以在WEB端提交导出任务的时候,往redis的LPUSH里面添加,然后通过python连接redis模块来实现队列任务,这个得看后期导出这块的业务时效性要求高不高了,如果要求极快的速度导出报表,那可以考虑现在这种方式。

下面这个是python脚本用到的相关模块

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os,time,datetime
from os.path import join,getsize
import MySQLdb #python 2.7 2.6支持
import ConfigParser
import logging
import csv
import codecs
import json
from random import Random
from qiniu import Auth,put_file,etag
import qiniu.config
import iso8601
import sys
reload(sys)
sys.setdefaultencoding('utf-8')



去打赏

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注