2020年5月

模型列表自动Where

if (!function_exists('model_where')) {
    function model_where(&$model, $where)
    {
        if (empty($where)) {
            return $model;
        }
        // 使用表结构分析, 自动将传进来的参数根据分析的表结构来决定查询条件
        $tableInfo = $model::DB()->select('SHOW FULL FIELDS FROM `' . $model->getTable() . '`');
        $tableInfo = object_to_array($tableInfo);
        $tableInfo = array_column_to_key($tableInfo, 'Field');
        $model = $model::query();
        foreach ($tableInfo as $key => $v) {
            if (strpos($tableInfo[$key]['Type'], 'int') !== false) {
                if (array_key_exists($key, $where) && is_numeric($where[$key])) {
                    $model = $model->where($key, intval($where[$key]));
                }
            } else if (strpos($tableInfo[$key]['Type'], 'char') !== false) {
                if (!empty($where[$key])) {
                    $model = $model->where($key, 'like', '%' . $where[$key] . '%');
                }
            } else if (
                strpos($tableInfo[$key]['Type'], 'decimal') !== false
                || strpos($tableInfo[$key]['Type'], 'timestamp') !== false
            ) {
                if (array_key_exists($key . '_from', $where) && $where[$key . '_from'] !== '') {
                    $model = $model->where($key, '>=', $where[$key . '_from']);
                }
                if (array_key_exists($key . '_to', $where) && $where[$key . '_to'] !== '') {
                    $model = $model->where($key, '<', $where[$key . '_to']);
                }
                if (array_key_exists($key, $where) && $where[$key] !== '') {
                    $model = $model->where($key, $where[$key]);
                }
            } else if (strpos($tableInfo[$key]['Type'], 'enum') !== false) {
                if (array_key_exists($key, $where)) {
                    $model = $model->where($key, $where[$key]);
                }
            }
        }
    }
}
  • 代码层使用示例
$where = RequestHelper::get();
$query = new AbcdModel()
model_where($query,$where);
return ResponseHelper::paginator($query->paginate(RequestHelper::getPerPage()), new \App\Transformers\AbcdModelTransformer());

限制不重复写入

if (!function_exists('sql_insert_once')) {
    // 写入限制, 确保写入只会被写入一次
    function model_sql_insert_once($table, $array, $existsSql = '')
    {
        $sql0 = [];
        $sql1 = [];
        foreach ($array as $kq => $kv) {
            $sql0[] = '`' . $kq . '`';
            if (is_null($kv)) {
                $sql1[] = 'NULL';
            } else {
                $sql1[] = '\'' . addslashes($kv) . '\'';
            }
        }
        // SELECT * FROM `' . $table . '` WHERE AND created_at>NOW()-INTERVAL 5 SECOND
        return ' INSERT INTO `' . $table . '`(' . implode(',', $sql0) . ') 
 SELECT ' . implode(',', $sql1) . ' FROM DUAL WHERE NOT EXISTS(' . $existsSql . ') ';

        /*
        DB::insert(DB::raw($tmpSql));
        $paymentParentId = DB::getPdo()->lastInsertId();
        if (empty($paymentParentId)) {
            error_responses('支付中 ...');
        }
        */

    }
}
  • 使用demo
$sql = model_sql_insert_once('course_orders', $courseOrderHeader, "SELECT * FROM course_orders WHERE 
help_code='" . $courseOrderHeader['help_code'] . "'
AND pid='" . $courseOrderHeader['pid'] . "'
AND class_id='" . $courseOrderHeader['class_id'] . "'
AND course_id='" . $courseOrderHeader['course_id'] . "'
AND state=1
AND created_at>NOW()-INTERVAL 60 SECOND");
// DB::insert(DB::raw($sql)) ;
$db = CourseOrderItem::DB();
$db->beginTransaction();
$db->insert($sql);
$orderId = DB::getPdo()->lastInsertId();
if (empty($orderId)){
    error_response('下单中');
}

针对delete操作, 常常会慢的严重, 明明查询出来需要删除的记录才70多条, 执行一次都得1分多钟

需求, 下单未支付同样的信息的, 删除除了最大的一条记录的信息(这边假设单人下单不超过2次相同的下单信息的, 我实际的操作是多执行几次就够了)
  • 原sql
delete from course_orders
where 
pid=1 
AND state=1
AND created_at<date_sub(now(),interval 7 DAY)
AND id IN (
    SELECT max_order_id FROM (
        SELECT use_uid,course_id,COUNT(1) AS num,MAX(order_id) AS max_order_id FROM `course_order_items`
        WHERE 
        pid=1 
        AND state=0
        AND created_at>'2020-01-01'
        GROUP BY use_uid,course_id
    ) a
    WHERE num>1
)

共 73 行受到影响

执行耗时   : 59.794 sec
传送时间   : 0.001 sec
总耗时      : 59.796 sec
  • 优化后
delete co from course_orders co inner join (
    SELECT max_order_id FROM (
        SELECT use_uid,course_id,COUNT(1) AS num,MAX(order_id) AS max_order_id FROM `course_order_items`
        WHERE 
        pid=1 
        AND state=0
        AND created_at>'2020-01-01'
        GROUP BY use_uid,course_id
    ) a
    WHERE num>1
) bb on pid=1 
AND state=1
AND created_at<date_sub(now(),interval 7 DAY)
AND co.id=bb.max_order_id

执行结果
共 73 行受到影响

执行耗时   : 0.066 sec
传送时间   : 0.008 sec
总耗时      : 0.075 sec

redis的一些命令和执行的返回值

方法含义示例命令返回值
select选择dbselect 0"OK"
字符串
SET字符串set string 888"OK"
__set string 888"OK"
GET字符串get string"888"
__get string1null
哈希
hset设置hash值hset aa abc 123"1"
__hset aa abcd 123"ERR wrong number of arguments for 'hset' command"
_warning注意, hash里面的value是唯一值, 不可相同, 其他hashkey的值如果和已有的相同则报错
__hset aa abc 1234"0"
_warning注意, hash里面key的value变化返回为0
集合
sadd集合SADD saddkey redis"1"
__SADD saddkey redis"0"
_warning这个里面保存的是唯一值的集合
有序集合
zadd有序集合ZADD zset 111 a1"1"
__ZADD zset 11 a1"ERR wrong number of arguments for 'zadd' command"
_warning注意, 有序集合里面的值不能通过该发放修改
__ZADD zset 111 a2"1"
__ZADD zset 111 a2"0"
ZREM移除有序集合的的setkeyZREM zset a1"1"
__ZREM zset a1"0"

普通写法

function get_chance($chance = [
    '和谐' => 3000,
    '爱国' => 5000,
    '敬业' => 110,
    '友善' => 1000,
    '富强' => 3000,
])
{
    $cardRange = [];
    $num = rand(0, array_sum(array_values($chance)));
    $total = 0;
    foreach ($chance as $key => $value) {
        $totalBefore = $total;
        $total += $value;
        if ($num >= $totalBefore && $num < $total) {
            return $key;
        }
        $cardRange[$key] = [$totalBefore, $total];
    }
    return $key;
}

在swoole环境中的写法, 兼容所有环境

function get_chance($chance = [
    '和谐' => 3000,
    '爱国' => 5000,
    '敬业' => 110,
    '友善' => 1000,
    '富强' => 3000,
])
{
    // 在协程中避免rand一段时间内值一致的问题
    $cardRange = [];
    $total = array_sum(array_values($chance));
    // $randValue = substr(explode(' ', microtime())[0], 0, 8);
    $randValue = strrev(substr(explode(' ', microtime())[0], 2, 6)) / 1000000;
    $rangeBefore = 0;
    $total2 = 0;
    foreach ($chance as $key => $value) {
        $total2 += $value;
        $rangeAfter = $total2 / $total;
        if ($randValue >= $rangeBefore && $randValue < $rangeAfter) {
            return $key;
        }
        $rangeBefore = $rangeAfter;
    }
    return $key;
}

校验数据和预期值的差异量

$t = [
    '记' => 300,
    '得' => 900,
    '我' => 150,
];
$r = [
    '记' => 0,
    '得' => 0,
    '我' => 0,
];
for ($k = 0; $k <= 10000; $k++) {
    $r[get_chance($t)]++;
}
var_dump($r);

方法说明方法返回值
json_search按给定字符串关键字搜索json,返回匹配的路径json_search('["10","2","1"]','all','1')"$[2]"
--查询路径2json_search('{"a":"123","b":{"c":"d","e":"f"}}','all','f')"$.b.e"
json_merge合并json数组或对象json_merge('["10","2"]','["10","9"]')["10", "2", "10", "9"]
JSON_MERGE_PRESERVE合并json数组或对象,代替json_merge用途JSON_MERGE_PRESERVE ('["10","2"]','["10","9"]')["10", "2", "10", "9"]
json_remove删除json数据json_remove('{"a":"123","b":"123"}','$.a'){"b": "123"}
--删除数组中值json_remove('["10","2","1","6"]','$[2]')["10", "2", "6"]
--删除当数组中存在某一个值时执行删除(注意, 当没有该值时, 这个查询的返回值为null)json_remove('["10","2","1","6"]',json_unquote(json_search('["10","2","1","6"]','all','1')))["10", "2", "6"]
--删除当数组中存在某一个值时执行删除(安全的操作, 只要数据量不大于99999就没问题)json_remove('["10","2","1","6"]',IFNULL(json_unquote(json_search('["10","2","1","6"]','all','11')),'$[9999999]'))["10", "2", "1", "6"]
json_keys提取json中的键值为json数组json_keys('{"a":"123","b":{"c":"d","e":"f"}}')["a", "b"]
json_length返回json的长度json_length('["10","2","10","9"]')4
--如果为复杂的对象结构, 这个长度依旧为根部key的个数json_length('{"a":"123","b":{"c":"d","e":"f"}}')2
json_unquote去除json字符串的引号,将值转成string类型
json_extract提取json值json_extract(openid,REPLACE(json_unquote(json_search(openid,'all','2')),'app_id','openid'))"oKTQ41OqjJ848_SPe7wbTZO9RmOE"openid 字段的数据包 '[{"app_id": "1", "openid": "oromp5ZEhkLz1BXELF-cxyqap-uw"}, {"app_id": "2", "openid": "oKTQ41OqjJ848_SPe7wbTZO9RmOE"}]'
json_replace替换值(只替换已经存在的旧值)实际测试如果值不存在, 并不会导致原有数据丢失
json_set只要目标值不为null,不为数组即可设置 json_set(data,"$.address","Guangzhou")