【问题标题】:Laravel join tables with JSON field, when some fields have no foreign key当某些字段没有外键时,Laravel 使用 JSON 字段连接表
【发布时间】:2020-10-14 15:20:28
【问题描述】:

我想通过存储在 JSON 字段中的外键连接 2 个表。该字段可以为空,所以我得到一个错误,我无法摆脱。

我有一个模特Transaction:

class Transaction extends BaseModel
{
    protected $casts = [
        'items' => 'json',
    ];

items 字段是 json:

[{"event_id":25,"article_nr":"000123","title":"My Event"}]

现在我想加入这些表进行查询:

Transaction::join('events','transactions.items->event_id', '=', 'events.id')
->get();

问题是,我有很多交易类型,所以 items 字段不一定包含event_id 键,甚至可以为空。

Eloquent 的 SQL 查询是:

select * from `transactions` inner join `events` on json_unquote(json_extract(`transactions`.`items`, '$."event_id"')) = `events`.`id`

所以对于空items-field 的行我得到错误:

3141 Invalid JSON text in argument 1 to function json_extract: "The document is empty." at position 0.

有没有办法改变我的联接并只选择包含items->event_id 键的行?

[编辑] 添加了 SQL 原始查询

【问题讨论】:

  • 甚至可以是空的 在这种情况下,精确值是多少。
  • 一个空字符串
  • 替换为有效的 JSON。例如,一个空数组。
  • 很好,现在看起来好多了。我没有收到任何错误。结果仍然是空的,但我想我可以克服这个。我猜我的语法是错误的。非常感谢。
  • 好吧,下一个问题:如果您查看数据中的括号,我的项目对象是一个数组。 eloquent 将其翻译为inner join events on json_unquote(json_extract(transactions.items, '$."[0]event_id"')) = events.id,但我需要inner join events on json_unquote(json_extract(transactions.items, '$[0]."event_id"')) = events.id

标签: php mysql laravel


【解决方案1】:

我搞定了……

在 Akina 的帮助下(感谢),他提到,所有字段都必须至少填充一个空的 json 对象:

Update transactions set items = '{}' where items = '' or items is null;

我能够加入表格:

Transaction::select(['transactions.*'])
                ->leftJoin('events',DB::raw("json_extract(transactions.items, '$[0].event_id')"),"=","events.id")

'$[0].event_id' 部分对于这个连接是特殊的,因为我的 event_id 在一个对象中,而这个对象在一个数组中:

[{"event_id":24}]

(注意外括号)

没有外部数组会很容易,我可以在没有原始 sql 的情况下完成它,例如:

Transaction::leftJoin('events','transactions.items->event_id', '=', 'events.id')

[编辑] 最终解决方案

通过 JSON 字段加入时,您会遇到性能问题。我的查询花了大约 160 秒。

最终的解决方案是从items 行创建一个生成列event_id。 MySQL“生成的列”从其表达式中自动填充,并在 items 字段更新时由 MySQL/MariaDB 自动更新。

ALTER TABLE transactions ADD event_id int(10) UNSIGNED AS (JSON_UNQUOTE(items->"$[0].event_id")) STORED;

为此的 laravel 迁移将是

$table->bigInteger('event_id')->unsigned()->storedAs('JSON_UNQUOTE(items->"$[0].event_id")');

现在我的查询只需要 0.2 秒而不是 160 秒。

【讨论】:

    猜你喜欢
    • 2021-09-24
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    相关资源
    最近更新 更多