首页 > 解决方案 > 为什么涉及不同年份时按日期范围查询不起作用?

问题描述

我在 Laravel 中有一个关于日期范围的查询,这让我想起了。最初查询工作没有错误,但我检测到如果日期范围在不同年份(12-2020 到 01-2021)之间,它不会返回任何记录。我有点厌倦了检查代码,我没有看到问题。

我在这里放置了将数据保存在数组中的查询,以及稍后我在返回值之前应用日期过滤器进行的检查。

public function getData($filters) {

    $tasks = $this->getModel()
        ->whereIn('tasks.department_id', $filters['departmentIds'])
        ->join('departments', 'departments.id', '=', 'tasks.department_id')
        ->join('deliverables as dev', 'dev.id', '=', 'tasks.deliverable_id')
        ->leftJoin('deliverables as sub', 'sub.id', '=', 'tasks.subdeliverable_id')
        ->leftJoin('workers as st_workers', 'st_workers.id', '=', 'tasks.start_worker_id')
        ->leftJoin('workers as end_workers', 'end_workers.id', '=', 'tasks.end_worker_id')
        ->leftJoin('type_task_statuses', 'type_task_statuses.id', '=', 'tasks.status_id')
        ->leftJoin('incidences', 'incidences.task_id', '=', 'tasks.id')
        ->select('tasks.id', 'departments.name as department', 'tasks.start_worker_id', 'tasks.end_worker_id', 'st_workers.short_name as start_worker',
        'end_workers.short_name as end_worker', 'tasks.plane', 'tasks.st', 'dev.name as deliverable', 'sub.name as subdeliverable',
            'tasks.quantity', 'tasks.other_deliverable', 'tasks.start_prevision_date', 'tasks.end_prevision_date', 'tasks.start_date',
            'tasks.end_date', 'tasks.prevision_hour', 'tasks.incurred_hour', 'type_task_statuses.display_name as status', 'type_task_statuses.color', 'tasks.advance', 'tasks.agreed',
            'tasks.disagreed_date', 'tasks.supervised', DB::raw('COUNT(incidences.id) as count_incidences'))
        ->groupBy('tasks.id');

    if (array_key_exists('fromDate', $filters) && $filters['fromDate']) {
        $tasks->whereRaw('(DATE_FORMAT(tasks.end_date, "%m-%Y") >= "'.$filters['fromDate'].'" OR tasks.end_date is NULL)');
    }
    if (array_key_exists('toDate', $filters) && $filters['toDate']) {
        $tasks->whereRaw('(DATE_FORMAT(tasks.end_date, "%m-%Y") <= "'.$filters['toDate'].'" OR tasks.end_date is NULL)');
    }

    return $tasks;
}

标签: phpmysqllaravel

解决方案


我建议将您的输入格式化为应用程序中的日期范围,而不是在数据库端应用格式化,请参阅我对anit-patterns的另一个答案

假设您有以下输入以匹配数据库中的日期

$filters['fromDate'] ='12-2020';
$filters['toDate'] = '01-2021';

这实际上相当于以下日期范围

从 2020-12-01 00:00:00 到 2021-01-31 23:59:59


因此,让我们尝试将过滤器输入转换为此范围格式

/** 2020-12-01 00:00:00 */
$fromDate = \Carbon\Carbon::createFromFormat('d-m-Y', '01-'.$filters['fromDate'])->startOfMonth()->format('Y-m-d H:i:s');
/** 2021-01-31 23:59:59 */
$toDate = \Carbon\Carbon::createFromFormat('d-m-Y', '01-'.$filters['toDate'])->endOfMonth()->format('Y-m-d H:i:s');

现在,您可以轻松地在查询中应用这些范围,并且在数据库方面,您不需要任何格式

if (array_key_exists('fromDate', $filters) && $filters['fromDate']) {
    $fromDate = \Carbon\Carbon::createFromFormat('d-m-Y', '01-' . $filters['fromDate'])->startOfMonth()->format('Y-m-d H:i:s');
    $tasks->where(function ($query) use ($fromDate) {
        $query->whereNull('tasks.end_date')
              ->orWhere('tasks.end_date', '>=', $fromDate);
    });
}

if (array_key_exists('toDate', $filters) && $filters['toDate']) {
    $toDate = \Carbon\Carbon::createFromFormat('d-m-Y', '01-' . $filters['toDate'])->endOfMonth()->format('Y-m-d H:i:s');
    $tasks->where(function ($query) use ($toDate) {
        $query->whereNull('tasks.end_date')
              ->orWhere('tasks.end_date', '<=', $toDate);
    });
}

推荐阅读