如何在 Laravel 5 的嵌套查询中使用 GroupBy?

How to use GroupBy in nested query in Laravel 5?

我在数据库中有四个 table,即套餐、固定装置、交易和固定装置交易。

以下是 table 结构的详细信息:

Packages: id, title
Fixtures: id, package_id, name
Deals: id, discound, description
Fixtures_Deal: id, fixture_id, deal_id, price

我需要获取套餐列表以及每个套餐在每个固定装置中提供的最低交易价格。

这是我在 phpMyAdmin 或 SQLYog 中 运行 的 mysql 查询,它工作得很好,但在 Laravel 中它给了我 "p.title' isn't in GROUP BY" 错误。

SELECT 
  p.title AS package_title,      
  tbl_min_value.min_price AS min_price 
FROM
  (SELECT 
    fixture_id,
    MIN(deal_price) AS min_price 
  FROM
    fixture_deal 
  GROUP BY fixture_id) AS tbl_min_value 
  JOIN fixtures AS f 
    ON f.id = tbl_min_value.fixture_id 
  RIGHT JOIN packages AS p 
    ON f.package_id = p.id 
GROUP BY p.id 

顺便说一下,我正在尝试通过在模型中使用以下方法来实现它:

return DB::statement('SELECT 
      p.title AS package_title,      
      tbl_min_value.min_price AS min_price 
    FROM
      (SELECT 
        fixture_id,
        MIN(deal_price) AS min_price 
      FROM
        fixture_deal 
      GROUP BY fixture_id) AS tbl_min_value 
      JOIN fixtures AS f 
        ON f.id = tbl_min_value.fixture_id 
      RIGHT JOIN packages AS p 
        ON f.package_id = p.id 
    GROUP BY p.id');

您收到的错误是因为 MySQL 5.7 移动到更符合 SQL99。您可以阅读一些关于 here 的内容,但它涉及一个名为 ONLY_FULL_GROUP_BY 的 SQL 模式设置,它确保任何被选择的非聚合列都在 GROUP BY 内。这对您的情况意味着什么?

好吧,您只需将 p.titletbl_min_value.min_price 添加到分组中即可使其正常工作,因为它们未聚合:

GROUP BY p.id, p.title, tbl_min_value.min_price

你可能想知道,"Why on earth would they do that?"。好吧,在使用 GROUP BY 之前很容易意外地 "collapse" 下数据,其中一行有多个可以显示的可能值,所以它只显示第一个。这使您可以使用 MIN()MAX() 等聚合函数找出要显示的内容,或者将其添加到分组中以表明您希望它们分开。

如果您不喜欢它的工作方式,您可以暂时禁用它:

# Run this query and copy the values
SELECT @@sql_mode;

# Remove "ONLY_FULL_GROUP_BY" and paste it in here where the XXX is
SET sql_mode = 'XXX';

或者您可以通过像这样编辑 [mysqld] 部分下的 my.cnf 文件来永久更改,其中 XXX 是 SELECT @@sql_mode; 减去 ONLY_FULL_GROUP_BY 的输出部分:

[mysqld]
sql_mode=XXX 

奇怪的是,SQL 查询在 phpMyAdmin 中运行良好,但在 Laravel 5.3 中却不起作用。您可以 post 带有示例数据的数据库结构,让我们看一下吗?

顺便说一句,我尝试关注 SQL 并且在 Laravel 5.2 中工作正常:

SELECT 
  p.title AS package_title,      
  tbl_min_value.min_price AS min_price 
FROM
  (SELECT 
    fixture_id,
    MIN(deal_price) AS min_price 
  FROM
    fixtures_deal 
  GROUP BY fixture_id) AS tbl_min_value 
  JOIN fixtures AS f 
    ON f.id = tbl_min_value.fixture_id 
  RIGHT JOIN packages AS p 
    ON f.package_id = p.id 
GROUP BY p.id 

我找到了另一种方法,使用 Eloquent 集合的 mapWithKeys 方法。

https://laravel.com/docs/5.3/collections#method-mapwithkeys

所以我分别取了两个query的数据,然后将两个collections的key映射到一个collection对象中。

示例代码:

$packages = Package::getPackages();

$deal_min_price = Deal::getMinimumPrice();

$packages_with_price = $packages->mapWithKeys(function ($packages) use ($deal_min_price) {
    return $packages['price'] => $deal_min_price['min_price'];
});

您可以在 mapWithKey's closure.

中使用 foreach 循环