如何解决 laravel eloquent 中与 sql_mode=only_full_group_by 不兼容的问题?
Posted
技术标签:
【中文标题】如何解决 laravel eloquent 中与 sql_mode=only_full_group_by 不兼容的问题?【英文标题】:How can I solve incompatible with sql_mode=only_full_group_by in laravel eloquent? 【发布时间】:2017-10-02 06:31:14 【问题描述】:我的 laravel 雄辩是这样的:
$products = Product::where('status', 1)
->where('stock', '>', 0)
->where('category_id', '=', $category_id)
->groupBy('store_id')
->orderBy('updated_at', 'desc')
->take(4)
->get();
执行时出现如下错误:
SQLSTATE[42000]:语法错误或访问冲突:1055 表达式 #1 的 SELECT 列表不在 GROUP BY 子句中并且包含非聚合 列“myshop.products.id”在功能上不依赖于 GROUP BY 子句中的列;这与 sql_mode=only_full_group_by (SQL: select * from
products
wherestatus
= 1 andstock
> 0 andcategory_id
= 5 group bystore_id
按updated_at
desc 限制 4) 排序
我该如何解决?
【问题讨论】:
【参考方案1】:我有一个类似的问题,并通过在数据库连接设置中禁用 mysql 严格模式来解决它。
'connections' => [
'mysql' => [
// Behave like MySQL 5.6
'strict' => false,
// Behave like MySQL 5.7
'strict' => true,
]
]
您可以在this blog post by Matt Stauffer找到更多配置设置
【讨论】:
如果我们设置 'strict' => false 那么它可能会产生安全漏洞 在浪费了一天之后来到这里,这解决了我的问题 @naabster 谢谢你,你可能为我节省了一天的时间来解决这个问题。问题:在这里不使用 strict 有什么缺点吗?另一条评论提到了安全漏洞。在我的情况下,原始 SQL 正在工作,但是当我通过 Laravel(查询生成器,5.6)时,我得到了“唯一完整组”错误。 花了一段时间才找到这个答案。最佳解决方案。太感谢了!请,OP。将此标记为正确答案。【参考方案2】:在文件夹 config => database.php 确保 mysql strict 为 false强>,像这样
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
如果 strict 为真,则将其设为假,然后通过在 cmd 中运行此命令来清除配置现金
php 工匠配置:清除
【讨论】:
谢谢您,您的'strict' => false
建议对我很有效【参考方案3】:
我通过添加“模式”选项并仅在 config => database.php
中设置我想要启用的模式解决了这个问题'mysql' => [
...
'modes' => [
'STRICT_ALL_TABLES',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ZERO_DATE',
'NO_ZERO_IN_DATE',
'NO_AUTO_CREATE_USER',
],
],
在this tutorial查看更多详情
【讨论】:
【参考方案4】:这是因为最新版本的 MySQL 在 group by
子句方面表现得与大多数 dbms 一样;一般规则是
如果您使用
group by
,则select
中的所有列必须存在于group by
中或由聚合函数聚合(sum
、count
、avg
等)
您当前的查询按store_id
分组,但由于您选择了所有内容,因此不遵守上述规则。
【讨论】:
如果我在 mysql 中运行这个查询:select * from products where status = 1 and stock > 0 and category_id = 5 group by store_id order by updated_at desc limit 4
,它可以工作。因此,错误似乎是通过雄辩的 laravel
我喜欢这个答案(它解释了为什么会发生这种情况),而不仅仅是“关闭严格模式”!更改查询似乎比关闭更全局的设置更合适和“本地”。谢谢【参考方案5】:
设置
'strict' => false
在您的 config/database.php 文件中。在数组中connections => mysql =>
就我而言,我使用的是 mysql 5.7 Laravel 5.7
【讨论】:
如果它不起作用,做一个php artisan config:clear
【参考方案6】:
您不应禁用strict
或删除ONLY_FULL_GROUP_BY
。问题是您的查询不明确。这可能不会对您的输出产生影响,或者可能会导致巨大的问题。你最好确定一下。
可以在Percona 上阅读很好的解释(总结如下)。
问题
考虑以下情况:
+----+--------------------+---------+---------------------+
| id | page_url | user_id | ts |
+----+--------------------+---------+---------------------+
| 1 | /index.html | 1 | 2019-04-17 12:21:32 |
| 2 | /index.html | 2 | 2019-04-17 12:21:35 |
| 3 | /news.php | 1 | 2019-04-17 12:22:11 |
| 4 | /store_offers.php | 3 | 2019-04-17 12:22:41 |
| 5 | /store_offers.html | 2 | 2019-04-17 12:23:04 |
| 6 | /faq.html | 1 | 2019-04-17 12:23:22 |
| 7 | /index.html | 3 | 2019-04-17 12:32:25 |
| 8 | /news.php | 2 | 2019-04-17 12:32:38 |
+----+--------------------+---------+---------------------+
现在我们要发出一个查询来计算访问次数最多的页面。这可能是你习惯写的:
SELECT page_url, user_id, COUNT(*) AS visits
FROM web_log
GROUP BY page_url
ORDER BY COUNT(*) DESC;
但是看看结果:
+-------------------+---------+--------+
| page_url | user_id | visits |
+-------------------+---------+--------+
| /index.html | 1 | 3 |
| /news.php | 1 | 2 |
| /store_offers.php | 3 | 2 |
| /faq.html | 1 | 1 |
+-------------------+---------+--------+
查询有效,但并不真正正确。很容易理解,page_url
是分组函数的列,是我们最感兴趣的值,我们希望计数是唯一的。此外,visits
列也不错,因为它是计数器。但是user_id
呢?此列代表什么?
我们对page_url
进行了分组,因此user_id
返回的值只是该组中的值之一。事实上,不仅是用户访问了 index.html,用户 2 和 3 也访问了该页面。那么我们应该如何看待这个价值呢?是第一个访客吗?是最后一个吗?
我们不知道正确的答案! user_id
列的值是组的随机项!
解决方案
您需要考虑是否需要groupBy()
中未使用的值。如果没有,那么只需使用 select()
来明确命名您需要的列。
如果您确实需要 groupBy()
中未使用的列,请使用聚合函数(如 ANY_VALUE()
或 GROUP_CONCAT()
或 MAX()
)作为 Laravel selectRaw
查询的一部分。然后,您可以确定您的查询提供了您所期望的。
所以在上面的例子中,你可以这样做:
SELECT page_url, ANY_VALUE(user_id), COUNT(*) AS visits
FROM web_log
GROUP BY page_url
ORDER BY COUNT(*) DESC;
或者在 Laravel 中:
WebLog::selectRaw('page_url', 'ANY_VALUE(user_id)', 'COUNT(*) AS visits')
->groupBy('page_url')
->orderBy('visits')
->get();
【讨论】:
【参考方案7】:我通过在 config/database.php 文件中设置模式解决了这个问题。
设置模式如下:
'modes' => [
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ENGINE_SUBSTITUTION',
]
用于mysql驱动
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
'modes' => [
'ONLY_FULL_GROUP_BY',
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ENGINE_SUBSTITUTION',
]
],
【讨论】:
它通过从列表中删除'ONLY_FULL_GROUP_BY'
来工作。【参考方案8】:
在 .env 文件中添加变量:DB_STRICT=false
。
并从以下位置替换文件:config/database.php,下一个代码'strict' => true
ON
'strict' => (env('DB_STRICT', 'true') === 'true' ? true : false)
.
祝你好运。
【讨论】:
我已经在 Lumen 8 中完成了它,但仍然无法正常工作,还有其他建议。谢谢。【参考方案9】:我做了什么作为一种变通方法,为了防止进一步的安全问题,我做了这样的事情:
public function getLatestModels ()
\DB::statement("SET SQL_MODE=''");
$latestInserted = Glasses::with('store.deliveryType','glassesHasTags','glassesHasColors','glassesHasSizes','glassesHasImages','glassesBrand','glassesMaterial')->whereRaw("store_id in (select distinct store_id from glasses)")->groupBy('store_id')->orderBy('created_at')->take(8)->get();
\DB::statement("SET SQL_MODE=only_full_group_by");
return $latestInserted;
这是其他答案的一种组合。 另外,如果您正在使用 “使用 Illuminate\Support\Facades\DB;” 您不需要在上面的那些 DB 语句中使用反斜杠。
这里唯一的缺点是我们对 db 进行了三个调用:(
附言 正如我看到@Felipe Pena 的回答,我猜第二个语句是不必要的
【讨论】:
【参考方案10】: #Have the following method in your helper file
if (!function_exists('set_sql_mode'))
/**
* @param string $mode
* @return bool
*/
function set_sql_mode($mode = '')
return \DB::statement("SET SQL_MODE=''");
然后调用 set_sql_mode('');就在雄辩/查询之前
【讨论】:
这会破坏整个设置。您可能只想从中提取 ONLY_FULL_GROUP_BY ..【参考方案11】:如前所述,将严格模式设置为 false 可能会产生安全错误,我正在做的是在需要它的查询之前将 sql_mode 设置为空。请注意,这是一个临时更改,一旦您的连接关闭(通过 laravel 请求),您将被设置为原始 sql_mode=only_full_group_by (或更高)。
DB::statement("SET sql_mode = ''");
干杯,编码愉快...
ps.:这不是 laravel 的错误,如果您尝试直接在数据库上执行此查询,您将面临相同的结果。这种解决方法适用于 mysql 以及第一个语句,并且再次将是临时会话更改,而不是永久性的。
【讨论】:
我使用的是 Lumen 8。我添加了这条语句 "DB::statement("SET sql_mode = '' ");"在每个控制器的构造函数中,它对我有用。谢谢亲爱的。【参考方案12】:检查查询:
Product::where('status', 1)
->where('stock', '>', 0)
->where('category_id', '=', $category_id)
->groupBy('store_id')
->orderBy('updated_at', 'desc')
->take(4)
->get();
在这里,您按store_id
对数据进行分组,并获取结果集中不允许的所有列。要解决它,请选择store_id
或对其进行聚合函数或将系统变量sql_mode=only_full_group_by
更改为SET sql_mode = ''
。
Reference
要在 Laravel 中设置这个,试试这个:
'strict' => false
in your config/database.php file. In array connections => mysql =>
【讨论】:
如果我在 mysql 中运行这个查询:select * from products where status = 1 and stock > 0 and category_id = 5 group by store_id order by updated_at desc limit 4
,它可以工作。因此,错误似乎是通过雄辩的 laravel
laravel如何设置sql模式?
如何在共享服务器上更改 SET sql_mode = ''?【参考方案13】:
要仅选择聚合列,请使用raw methods provided by Laravel 之一。例如:
Product::selectRaw('store_id')
->where('status', 1)
->groupBy('store_id')
->get();
【讨论】:
以上是关于如何解决 laravel eloquent 中与 sql_mode=only_full_group_by 不兼容的问题?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Laravel Eloquent 和 Laravel Query Builder 进行联合查询?