【发布时间】:2022-10-21 23:00:53
【问题描述】:
所以这是我的情况。我有一个主表 payment_transactions 有近 100 万条记录。
我正在使用连接和 where 子句从该表中获取数据,并且有 Laravel paginate 方法,
但是这种方法耗时太长,经过调查,我发现它的计数方法仅用于计数就需要4到5秒的时间。
那么如何优化和加速这个查询,特别是有什么方法可以提高分页方法的速度呢?
Note: 我不能使用 simplePaginate 因为前端有一个数据表,我需要一个总数。
所以对于分页,两个查询运行 1是主查询,另一个是count,我觉得count查询很费时间。
这是getQueryLog之后的计数查询
select count(*) as aggregate from `payment_transactions`
left join `users` as `U` on `U`.`id` = `payment_transactions`.`user_id`
left join `coupons` as `C`
on `C`.`id` = `payment_transactions`.`coupon_id`
where `payment_transactions`.`refund_status` = 'NO_REFUND'
and `payment_transactions`.`transaction_type`
in ('BOOKING','SB_ANPR','QUERCUS_ANPR','CANDID_ANPR','SB_TICKET',
'ORBILITY_TICKET','TOPUP,CREDIT','DEBIT','GIFT')
and `payment_transactions`.`status` != 'INITIATED'
这是我的代码示例:
//Get Transactions data
public function adminTransactions(Request $request)
{
$selectableFields = [
'payment_transactions.id', 'payment_transactions.transaction_id AS transaction_id',
'payment_transactions.refund_status',
'payment_transactions.created_at', 'payment_transactions.response_data', 'payment_transactions.status',
'payment_transactions.transaction_type', 'payment_transactions.payment_mode','payment_transactions.payment_source',
'payment_transactions.subtotal', 'payment_transactions.service_fees', 'C.coupon_code','C.amount AS coupon_value',
DB::raw("IF(payment_transactions.refund_remarks='NULL','-NA-',payment_transactions.refund_remarks) as refund_remarks"),
DB::raw("IF(payment_transactions.transaction_type='TOPUP' AND payment_transactions.coupon_id IS NOT NULL
AND payment_transactions.coupon_id!=0,
payment_transactions.amount + C.amount,payment_transactions.amount) as amount"),
DB::raw("CONCAT(U.first_name,' ',U.last_name) AS username"), 'U.id AS user_id',
DB::raw("JSON_UNQUOTE(json_extract(payment_transactions.response_data, '$.description')) AS description"),
DB::raw("payment_transactions.invoice_id"),
DB::raw("JSON_UNQUOTE(json_extract(payment_transactions.response_data, '$.Data.PaymentID')) AS upay_payment_id"),
];
return PaymentTransactions::select($selectableFields)
->with('homeScreenMessages:payment_transaction_id,from_name,message,amount')
->leftJoin('users AS U', 'U.id', '=', 'payment_transactions.user_id')
->leftJoin('coupons AS C', 'C.id', '=', 'payment_transactions.coupon_id')
->where(DB::raw("CONCAT(U.first_name,' ',U.last_name)"), 'like', "%{$request->input('query')}%")
->orWhere('U.id', $request->input('query'))
->orWhere("U.phone_number", "LIKE", "%" . $request->input('query') . "%")
->orWhere("U.email", "LIKE", "%" . $request->input('query') . "%")
->orWhere('payment_transactions.id', $request->input('query'))
->orWhere('payment_transactions.transaction_id', $request->input('query'));
}
//Paginate function
public function paginationCalculate($queryObject, $request) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['name']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$pageNumber = ($start + $rowperpage) / $rowperpage;
if(!empty($columnName)) {
$queryObject->orderBy($columnName, $columnSortOrder);
}
$records = $queryObject->paginate($rowperpage, ['*'], 'page', $pageNumber)->toArray();
return array(
"draw" => intval($draw),
"recordsFiltered" => $records['total'],
"recordsTotal" => $records['total'],
"data" => $records['data']
);
}
【问题讨论】:
-
我假设您已经索引了正确的列?如果删除所有包含
$request->input('query')的行,您可以测试查询需要多长时间。如果速度足够快,那么您就知道这是导致速度缓慢的原因。还有其他方法可以在数据库中搜索,但在您查看之前,您首先需要确认这确实是原因。 -
@KIKOSoftware,我在数据库中添加了所有可能的
index,并在没有$request->input的情况下进行了测试,在getQueryLog之后我发现分页计数需要时间,所以我删除了它只是为了测试并注意几乎减少了 4 到 5 秒的时间。 -
抱歉,我阅读了您的回答,但它仍然不知道删除所有包含
$request->input('query')的行有什么影响。请注意,我写的是“删除所有行”,而不仅仅是空的$request->input('query')。如果您使用过getQueryLog,并且您认为您知道问题所在,请告诉我们,最好是通过编辑您的问题,而不是在评论中。你在说什么样的“分页计数”? -
感谢您编辑您的问题。好的我明白了。我注意到计数查询连接了
users和coupons表,但它们实际上并未用于创建计数。如果删除它们,然后确保transaction_type和status列有索引,那么该查询应该很快。请注意,您可以将多个列组合成一个索引。 -
@KIKOSoftware,感谢您的回复,我会按照您的建议尝试。但是我在添加索引后尝试过,但我认为没有像你说的那样尝试使用复合索引。
标签: php mysql laravel optimization pagination