【问题标题】:How can I solve incompatible with sql_mode=only_full_group_by in laravel eloquent?如何解决 laravel eloquent 中与 sql_mode=only_full_group_by 不兼容的问题?
【发布时间】: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 where status = 1 and stock > 0 and category_id = 5 group by store_idupdated_at desc 限制 4) 排序

我该如何解决?

【问题讨论】:

    标签: php mysql laravel-5 laravel-5.3 laravel-eloquent


    【解决方案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 strictfalse强>,像这样

    '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 中或由聚合函数聚合(sumcountavg 等)

      您当前的查询按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();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-05-12
            • 1970-01-01
            • 1970-01-01
            • 2020-02-29
            • 2016-10-03
            • 2018-03-12
            • 2016-07-12
            相关资源
            最近更新 更多