QueryBuilder – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Sat, 25 Apr 2026 03:57:21 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 Spatie\QueryBuilder 中,查询 SQL 强制使用索引 https://www.shuijingwanwq.com/2026/04/25/9583/ https://www.shuijingwanwq.com/2026/04/25/9583/#comments Sat, 25 Apr 2026 03:56:41 +0000 https://www.shuijingwanwq.com/?p=9583 浏览量: 213

1、参考:列表接口响应超时的优化 。在 Spatie\QueryBuilder 中,查询 SQL 强制使用索引 。实现如下

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Arr;

        // 当筛选字段包含:交运状态、交运类型、操作来源 时,强制使用组合索引 order_shipping_logs_sag_os_ss_st_index
        if (Arr::hasAny($criteria['filter'], ['shipping_status','shipping_type', 'operated_source'])) {
            Log::info(
                '$criteria1',
                $criteria['filter']
            );
            $builder->from(DB::raw('order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)'));
        }

2、生成的 SQL 如下,符合预期。如图1

生成的 SQL 如下,符合预期
select
  count(*) as aggregate
from
  order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)
where
  `order_shipping_logs`.`shipping_type` = 2
  and `shipping_at_gmt` >= '2024-07-22 03:39:54'
  and `shipping_at_gmt` <= '2024-10-22 03:39:54';


select
  count(*) as aggregate
from
  order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)
where
  `order_shipping_logs`.`operated_source` in (3)
  and `order_shipping_logs`.`shipping_type` = 2
  and `order_shipping_logs`.`shipping_status` = 3
  and `shipping_at_gmt` >= '2024-07-22 03:47:16'
  and `shipping_at_gmt` <= '2024-10-22 03:47:16'
]]>
https://www.shuijingwanwq.com/2026/04/25/9583/feed/ 1
在 Laravel 9 中,基于 Spatie\QueryBuilder\QueryBuilder 来生成查询 SQL,同一字段可支持:partial、exact https://www.shuijingwanwq.com/2024/04/20/8548/ https://www.shuijingwanwq.com/2024/04/20/8548/#respond Sat, 20 Apr 2024 02:38:51 +0000 https://www.shuijingwanwq.com/?p=8548 浏览量: 52 1、参考:在 Laravel 9 中,在异步队列中复用 Spatie\QueryBuilder\QueryBuilder 来生成查询 SQL



    /**
     * 查询生成器
     * @param array $criteria
     * @return QueryBuilder
     */
    public function QueryBuilder(array $criteria): QueryBuilder
    {
        return QueryBuilder::for(OrderShippingLog::class, new Request($criteria))
            ->allowedFilters([
                AllowedFilter::exact('shop_plat_id'),
                AllowedFilter::exact('shop_id'),
                AllowedFilter::exact('logistic_channel_id'),
                AllowedFilter::exact('logistic_company_id'),
                AllowedFilter::exact('operated_source'),
                AllowedFilter::exact('plat_order_no'),
                AllowedFilter::exact('logistic_freight_no'),
                AllowedFilter::exact('logistic_freight_transfer_no'),
                AllowedFilter::exact('logistic_freight_inner_no'),
                AllowedFilter::exact('logistic_virtual_number'),
                AllowedFilter::exact('shipping_type'),
                AllowedFilter::exact('shipping_status'),
                AllowedFilter::scope('shipping_at_gmt_start'),
                AllowedFilter::scope('shipping_at_gmt_end'),
                AllowedFilter::scope('operated_at_gmt_start'),
                AllowedFilter::scope('operated_at_gmt_end'),
            ])
            ->with('shopPlat:id,title')
            ->with('shop:id,title')
            ->with('order.orderItemLogisticsFeatures')
            ->with('order.urgentType:id,title,color')
            ->with('logisticChannel:id,title')
            ->with('operatorUser:id,name,number')
            ->defaultSort('-id');
    }


2、现在的实现方案,针对某一个字段,只能够使用一种过滤器,或者 partial ,或者 exact。参考:在 Yii 2.0 中,实现过滤器的请求参数形式


{
    "filter": {
        "plat_order_no": [
            "GM20240202055938",
            "GM20240131081251"
        ]
    },
    "sort": "-operated_at_gmt",
    "per_page": 20,
    "page": 1
}




{
    "filter": {
        "plat_order_no": [
			"like": [
				"GM20240202055938",
				"GM20240131081251"
			]
        ]
    },
    "sort": "-operated_at_gmt",
    "per_page": 20,
    "page": 1
}


3、针对需要 单个模糊检索,多个精确检索 的字段,决定验证其类型为数组,然后判断数组元素的数量,如果大于 1 ,则精确检索,否则,模糊检索。最终实现如下。


$validator = Validator::make(
            $params,
            [
                'filter.plat_order_no' => 'array',
                'filter.plat_order_no.*' => 'max:100',
            ],
            [
                'filter.plat_order_no.array' => '原订单的平台订单号必须是数组',
                'filter.plat_order_no.*.max' => '原订单的平台订单号最大长度是100',
            ]
        );
        if ($validator->stopOnFirstFailure()->fails()) {
            throw new BusinessException(BusinessException::MODULE_ORDER, $validator->getMessageBag()->first());
        }

        return $validator;




        // 单个模糊检索,多个精确检索:订单号、交易号、退货单号、退货快递单号、货运单号、商品SKU
        // 键为请求参数,值为 SQL 查询字段
        $fields = [
            'plat_order_no' => 'plat_order_no',
        ];

        foreach ($fields as $key => $field) {
            if (isset($criteria['filter'][$key])) {
                if (count($criteria['filter'][$key]) > 1) {
                    // 多个精确检索
                    $allowedFilters[] = AllowedFilter::exact($field);
                } else {
                    // 单个模糊检索
                    $allowedFilters[] = AllowedFilter::partial($field);
                }

                if ($key != $field) {
                    $criteria['filter'][$field] = $criteria['filter'][$key];
                    unset($criteria['filter'][$key]);
                }
            }
        }

        $builder = QueryBuilder::for(ReturnOrder::class, new Request($criteria))
            ->select('return_orders.*')
            ->allowedFilters($allowedFilters)
            ->defaultSort('-id');


   ]]>
https://www.shuijingwanwq.com/2024/04/20/8548/feed/ 0