Laravel Eloquent 在 JSON 列中查找日期在某个范围内的所有记录

Posted

技术标签:

【中文标题】Laravel Eloquent 在 JSON 列中查找日期在某个范围内的所有记录【英文标题】:Laravel Eloquent find all records where a date is within a range in JSON column 【发布时间】:2021-08-22 12:13:59 【问题描述】:

我正在尝试使用 Eloquent 查询数据库中的记录表,并仅检索 json 列中至少有 1 条记录(由对象数组组成)在给定日期范围内的记录。

日期列如下所示:

[
  'date': '2021-01-01',
  'date': '2021-01-02',
  'date': '2021-01-03',
]

如果我通过了“2021-01-01”的开始日期,则它需要获取 dates.*.date 等于或晚于该开始日期的所有记录。

结束日期也是如此。

我有不同类型的语法,例如:

$this->where('dates->[*]->date', '<=', date($value));
$this->where('dates->*->date', '<=', date($value));
$this->where('dates.*.date', '<=', date($value));

似乎没有任何效果。

我做错了什么?

【问题讨论】:

您使用的是哪个 mysql 版本?在 8.x 中,您可以使用 JSON_TABLE 函数将日期数组提取到表中并对其执行查询 如果没有任何效果,只需通过 php 完成。您可以创建日期索引,然后与之匹配 【参考方案1】:

您可以将whereJsonContains 第二个参数作为数组传递

Model::whereJsonContains('dates',['date'=>'2018-01-01'])->get();

【讨论】:

但这会寻找完全匹配?!如何返回 2018-01-01 及之后/之前的所有日期? @MarcusChristiansen.yes 你的权利。我尝试了很多方法来小于或大于但无法成功。如果我有时间我会再次检查【参考方案2】:

这是一个原始 SQL 版本,我不太了解 Eloquent,但我很确定你可以执行原始 SQL。

这是你的桌子

CREATE TABLE MyTable(id int, dates varchar(100));
INSERT INTO MyTable(id, dates) values(1, '["date": "2020-01-01","date": "2020-01-02","date": "2020-01-03"]');
INSERT INTO MyTable(id, dates) values(2, '["date": "2021-01-01","date": "2021-01-02","date": "2021-01-03"]');
INSERT INTO MyTable(id, dates) values(3, '["date": "2022-01-01","date": "2022-01-02","date": "2022-01-03"]');

你可以使用这个 SQL

SELECT * FROM MyTable 
WHERE EXISTS (
    SELECT * 
    FROM JSON_TABLE(
            JSON_EXTRACT(dates, "$[*].date")
            , "$[*]" 
            COLUMNS(jsondate date path '$'
        )
    ) as fn 
    WHERE jsondate >= '2021-06-01'
);

父查询实际上是直截了当的,但子查询更复杂。它首先使用 MySQL 中的 JSON_EXTRACT() 函数,该函数采用 JSON 和“路径”来指定要返回的 JSON 值:在我们的例子中,我们的 json 对象中的每个 date 字段,例如对于第一行,它将返回: ["2020-01-01", "2020-01-02", "2020-01-03"].

有了这个 JSON 数组,您现在可以使用 JSON_TABLE() 函数创建一个匿名表,该表也采用 JSON 和新表的定义。我们现在有一个表,其中一列 jsondate 每一行是一个日期,因此我们可以使用一个简单的 WHERE 子句来检查日期是否大于您想要的日期。

Example

【讨论】:

【参考方案3】:

您可以使用多个orWhereJsonContains() 约束

$query->where(function ($query) use ($dates) 
   foreach ($dates as $date) 
       $query->orWhereJsonContains('dates', ['date' => $date]);
   
);

【讨论】:

如果您只过了开始日期或结束日期,这将不起作用。 您需要遍历日期。 $dates = new DatePeriod( new DateTime('2010-10-01'), new DateInterval('P1D'), new DateTime('2010-10-05') );【参考方案4】:

您应该尝试 whereRawJSON_EXTRACT -

$this->whereRaw('JSON_EXTRACT(`dates` , "$[*].date") <= ?', [date($value)]);

我认为这将适用于您的情况,请尝试让我知道它是否有效。

代码链接-https://phpsandbox.io/n/calm-term-y1yo-upqak

要运行代码,请使用终端打开修补程序

php artisan tinker

app(App\Http\Controllers\UserController::class)->testingJsonExtract()

供您参考 - https://dev.mysql.com/doc/refman/5.7/en/json.html#json-paths

【讨论】:

【参考方案5】:
$this->whereDate("json_extract('dates', '$.date')", '>', $date)->get();

正如您在上面看到的,我使用了json_extract() 方法,这是 MySQL 的从 JSON 列中提取字段的函数,因为我不能简单地使用箭头运算符指示 eloquent。

$this->whereDate('dates->date', '>', $date)->get();

我必须明确告诉 eloquent 提取 json 字段然后继续。

【讨论】:

这会假设有一个直接的孩子称为 date of dates 但 dates 是一个对象数组。 @MarcusChristiansen,尝试改用$[*].date【参考方案6】:

JSON_TABLE 可能是唯一(好的)纯 SQL 方法。这需要 MySQL 8.0.4 或更高版本。这是 Eloquent:

$this->whereExists(function($query) 
        $query->fromRaw(
            'json_table(json_extract(`dates`, "$[*].date"), "$[*]" columns(`json_date` date path "$")) as `sub`'
        )
        ->where('json_date','>=','2021-01-01');
    )->get();

解释:

json_extract$[*].date 作为路径参数将数据转换为常规数组["2020-01-01", "2020-01-02", "2020-01-03"])

json_table 将 json 数据转换为常规 sql 行:

json_date
2020-01-01
2020-01-02
2020-01-03

应用 where 子句并将结果行过滤到仅在 json 表中具有现有 json_date 行的行。

【讨论】:

我当然不知道;我不确定我是否遗漏了什么?其他答案没有考虑日期范围和每行/json列的多个日期,这就是我建议json_table的原因。 你能提供一个可行的例子吗? 我要做的是在模型上创建一个范围,让您查询日期而无需每次都编写原始 sql:gist.github.com/LowSociety/4ef7a901b8733a7d3d359fad82c7b6c7 然后像这样查询模型:gist.github.com/LowSociety/921c8cf91ba613180043075c479ae080【参考方案7】:

尝试以下方法:

Model::whereJsonContains('dates', ['starts_at' => '2021-01-01'])->get();

类似地,对于不超过某个值的日期:

Model::whereJsonContains('dates', ['ends_at' => '2021-01-01'])->get();

【讨论】:

以上是关于Laravel Eloquent 在 JSON 列中查找日期在某个范围内的所有记录的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Eloquent 支持 MariaDb 动态专栏

使用 laravel 构建器的 JSON 列查询

无法使用 Laravel 雄辩模型更新 json 列

Laravel 在急切加载时使用 Eloquent 选择特定列

Laravel 5 eloquent 的 CONCAT 列

如何在 laravel 的 eloquent 中转换表列数据?