Laravel 的查询构建器 JSON 选择器 `field->key` 导致语法错误
Posted
技术标签:
【中文标题】Laravel 的查询构建器 JSON 选择器 `field->key` 导致语法错误【英文标题】:Laravel's query builder JSON selector `field->key` causes syntax error 【发布时间】:2018-02-27 04:01:06 【问题描述】:所以,我想通过比较某个 ID 与 data
列来查询 Laravel 中的 notifications
表。这是data
列的样子:
"Message": "some message",
"id": 3
现在,我需要选择 ID 等于 3 的所有通知。这是我尝试的方法:
DB::table('notifications')->where('data->id', '3')->get();
但这会引发以下错误:
SQLSTATE[42000]:语法错误或访问冲突:1064 你有一个 SQL 语法错误;检查与您对应的手册 MariaDB 服务器版本,用于在 '>'$."id"' = 附近使用正确的语法 ?在第 1 行 (SQL: select * from
notifications
wheredata
->'$."id"' = 3)
我在这里疯了,谁能帮帮我?
【问题讨论】:
Laravel 将 JSON 存储为文本,所以不能这样查询。您需要更改存储此信息的方式才能查询它,否则您将需要像data LIKE '%"id": 3%'
这样进行全文搜索,但是这种查询非常慢。
看看这个:***.com/questions/44669673/…
【参考方案1】:
问题是只有mysql支持->
操作符,那么在MariaDB上查询会失败,但是:
MariaDB 和 MySQL 都支持 JSON_EXTRACT
函数,它解析 JSON 并从中获取值。
你可以通过这样的查询来解决它:
SELECT * FROM notifications WHERE JSON_EXTRACT(`notifications.data`, "$.id") = 5
要使用Laravel's Query Builder,您需要使用DB::raw
方法:
DB::table('notifications')->where(DB::raw('JSON_EXTRACT(`notifications.data`, "$.id")'), '=', 3);
请尝试一下,如果出错了告诉我。
注意:JSON_EXTRACT()
仅适用于MySQL >= 5.7
或MariaDB >= 10.2.3
感谢@Sepehr,我不知道->
是MySQL 运算符,也不存在JSON 类型。 :-)
【讨论】:
即使您的答案“围绕”问题,它也不能回答问题。您关于 Laravel 将 JSON 存储为字符串 is false 的声明,它是 JSON 类型的字段,而不是字符串类型的字段。除此之外,从 Laravel 5.3 开始,您可以定位 JSON 类型的字段键 using the->
notation,而不是在您建议的原始查询中使用 JSON_EXTRACT
。
您仍然没有回答实际问题。 JSON type 和 ->
operator 都存在。但是您在存储类型上是对的; Laravel 的 JSON 类型字段被创建为 longtext
类型字段并且是字符串。但是您仍然应该能够使用箭头符号查询它们而不会出现语法错误。它在docs。
实际的问题是,获得built by Laravel's MySQL grammar 的->
extraction operator 在对MariaDB 执行时会抛出该语法错误。
我刚刚确认了这一点,MariaDB 不支持->
。然而,同样适用于 MySQL 安装。
@sepehr,正如我在回答中所说,JSON_EXTRACT 解决了问题,对吧? :-)【参考方案2】:
问题在于->
,我认为您有一个名为$data 的var,其中包含一些集合。
如果是这样,那么正确的方法是:
DB::table('notifications')->where($data->id, '3')->get();
或者,如果您有一个与 通知 表相关的模型,那么:
Notification::where($data->id, '3')->get();
如果 Model
被称为 Notification
(遵循 Eloquent 约定)
但如果你试图找到所有 ID 都等于 3,那么只需:
DB::table('notifications')->where('id', '3')->get();
【讨论】:
OP 问题中的->
表示法是 special Laravel notation 用于访问 JSON 字段的键。最终会翻译成MySQL对应的operator。话虽如此,您的答案与问题完全无关。欢迎来到 SO。
我确实了解 JSON 我想我误解了这个问题【参考方案3】:
您的查询没有问题。这是你的环境。
问题
Laravel 的 MySqlGrammar
将字段名称中的 field->key
表示法(在 Laravel 端)转换为 field->'$.key'
样式的提取(在 MySQL 端):
/**
* Wrap the given JSON selector.
*
* @param string $value
* @return string
*/
protected function wrapJsonSelector($value)
$path = explode('->', $value);
$field = $this->wrapValue(array_shift($path));
$path = collect($path)->map(function ($part)
return '"'.$part.'"';
)->implode('.');
// Here:
return sprintf('%s->\'$.%s\'', $field, $path);
我刚刚确认 MariaDB 不支持 ->
extraction operator 作为 JSON_EXTRACT()
函数的别名。但是,相同的查询适用于普通 MySQL 5.7 服务器。
假设这个test
表:
╔════╤══════════════════╗
║ id │ payload ║
╟────┼──────────────────╢
║ 1 │ "a": 1, "b": 2 ║
╚════╧══════════════════╝
使用->
提取运算符的查询:
SELECT payload->"$.b" FROM test;
在 MariaDB 10.2.8 上失败,而在 MySQL 5.7.19 服务器上生成正确的2
。
解决方案
正确的解决方案取决于您在生产中使用的内容。
替换 MariaDB
如果您使用的是 MySQL,请在您的开发环境中将 MariaDB 替换为 MySQL。在由 homebrew 管理的 macOS 机器上,它很简单:
brew services stop mysql
brew uninstall mariadb
brew install mysql
brew services start mysql
您的数据将保持不变。
重写您的查询
但是,如果您在生产环境中使用 MariaDB,则需要重写查询以将 JSON_EXTRACT()
函数用作 Elias already mentioned。如您所见,您需要更详细地使用 Laravel API。
上面的查询是:
SELECT JSON_EXTRACT(payload, "$.b") FROM test;
【讨论】:
【参考方案4】:我为 json 支持制作了 Laravel MariaDB 驱动程序。在这里获取:ybr-nx/laravel-mariadb
【讨论】:
有趣!这应该是核心。以上是关于Laravel 的查询构建器 JSON 选择器 `field->key` 导致语法错误的主要内容,如果未能解决你的问题,请参考以下文章