带有MIN的MySQL GROUP BY - 不正确的列数据

时间:2013-06-20 19:56:52

标签: mysql sql group-by min

我看过这里:Selecting all corresponding fields using MAX and GROUP BY以及关于SO的类似网页,但我似乎无法让所有字段正确排列。

我觉得我正处于弄清楚这一点的风口浪尖,但也许我正朝着错误的道路前进,需要以不同的方式看待它。

我想要的是每个卧室的每个属性名称租金最低的单位,其合并标志设置为1。

我的SQL小提琴:http://sqlfiddle.com/#!2/881c41/2

All rental units with merge = 1 query result

上面的图片是通过此查询获得的:

SELECT ru.id, run.name, ru.rent, ru.bedrooms
FROM rental_units AS ru
JOIN rental_unit_names AS run
on run.id = ru.name_id
WHERE run.merge = 1
ORDER BY run.name ASC, ru.bedrooms ASC, ru.rent ASC

Rental units with merge = 1 grouped by property name and bedrooms by min value query result

上面的图片是此查询的结果:

SELECT ru.id, run.name, ru.rent, MIN(ru.rent) AS min_rent, ru.bedrooms
FROM rental_units AS ru
JOIN rental_unit_names AS run
on run.id = ru.name_id
WHERE run.merge = 1
GROUP BY ru.name_id, ru.bedrooms
ORDER BY run.name ASC, ru.bedrooms ASC, ru.rent ASC, ru.id ASC

在大多数情况下,所有看起来都很精致和花花公子,直到你看第4行。租金值不对齐而id应该 6 而不是 5

下图是我想要的结果。

desired results

::编辑1 ::

我是否需要创建一个包含2列的链接表,其中一列中的出租单位ID和另一列中的出租单位名称ID?或者至少以某种方式将其作为派生表?

4 个答案:

答案 0 :(得分:1)

一般情况下,除非您尝试执行某种MySQL“魔法”,否则应始终按 SELECT列表中的每个非聚合非常量列进行分组。

在您的情况下,最好的方法是获取(名称,#卧室,最低租金)列表,然后找到与这些值匹配的所有行 - 换句话说,所有行(名称,#卧室,租金)与最低租金匹配:

SELECT ru.id, run.name, ru.rent, ru.bedrooms
FROM rental_units ru
JOIN rental_unit_names run ON run.id = ru.name_id
WHERE run.merge = 1
  AND (run.name, ru.bedrooms, ru.rent) IN (
    SELECT inrun.name, inru.bedrooms, MIN(inru.rent)
    FROM rental_units inru
    JOIN rental_unit_names inrun ON inrun.id = inru.name_id
    WHERE inrun.merge = 1
    GROUP BY inrun.name, inru.bedrooms)

此查询将按名称/卧室提供所有最低租金单位。样本数据在几个地方的关系最低。要仅包含其中一个“关联”行(具有最低rental_units.id的行),请尝试相反 - 唯一的更改是第一行的MIN(ru.id)和整体{{1}的添加在最后一行:

GROUP BY

答案 1 :(得分:1)

这是因为group by中包含的 列来自不确定的行。 MySQL documentation在这一点上非常明确:

  

MySQL扩展了GROUP BY的使用,以便选择列表可以引用   未在GROUP BY子句中命名的非聚合列。这意味着   前面的查询在MySQL中是合法的。您可以使用此功能   通过避免不必要的列排序来获得更好的性能   分组。但是,这主要适用于每个中的所有值   GROUP BY中未命名的非聚合列对于每个列都是相同的   组。服务器可以自由选择每个组中的任何值,所以   除非它们相同,否则所选择的值是不确定的。   此外,不能从每个组中选择值   受添加ORDER BY子句的影响。

因为我刚刚在另一个post上回答了这个问题,我建议你去看看。

编辑:

以下是将substring_index() / group_concat()方法应用于查询的方法:

SELECT substring_index(group_concat(ru.id order by rent), ',', 1) as id,
       run.name, MIN(ru.rent) AS min_rent, ru.bedrooms
FROM rental_units ru JOIN
     rental_unit_names run
     on run.id = ru.name_id
WHERE run.merge = 1
GROUP BY ru.name_id, ru.bedrooms
ORDER BY run.name ASC, ru.bedrooms ASC, ru.rent ASC, ru.id ASC

答案 2 :(得分:1)

SELECT min(ru.id) as id, run.name, ru.rent, ru.rent AS min_rent, ru.bedrooms
FROM rental_units AS ru
JOIN rental_unit_names AS run
on run.id = ru.name_id
WHERE run.merge = 1
and ru.rent = 
(select min(ru1.rent) from rental_units AS ru1
JOIN rental_unit_names AS run1
on run1.id = ru1.name_id
where run.name = run1.name
and ru.bedrooms = ru1.bedrooms
and run1.merge = 1)
group by run.name, ru.rent,min_rent, ru.bedrooms
ORDER BY run.name ASC, ru.bedrooms ASC, ru.rent ASC, ru.id ASC;

完美作品.. !!

答案 3 :(得分:1)

由于mysql group-by extensions中解释的原因,您的查询会得出错误的结果。

您可以尝试将您的组按部分放在子查询中,然后联接回同一个表以获取您可能需要的其他隐藏列(如id),最后加入名称表以获取房间名称。您可以使用自我联接的最低ID来解决关系。

SELECT ro.id, run.name, ro.rent, ro.bedrooms
FROM 
( SELECT name_id, bedrooms, MIN(rent) AS cheapest_rent
  FROM rental_units 
  GROUP BY name_id, bedrooms ) AS ru
JOIN rental_units ro
ON ro.id = ( SELECT ri.id FROM rental_units ri
              WHERE ri.name_id = ru.name_id
              AND ri.bedrooms = ru.bedrooms
              AND ri.rent = ru.cheapest_rent
              ORDER BY ri.name_id, ri.bedrooms, ri.rent, ri.id
              LIMIT 1 )
JOIN rental_unit_names run ON ro.name_id = run.id
WHERE run.merge = 1
ORDER BY run.name ASC, ro.bedrooms ASC, ro.rent ASC

Sqlfiddle here

注意模式的细微变化,我在(name_id,卧室,租金)上添加了一个索引,以帮助分组和自联接(检查sqlfiddle上的执行计划),尽管由于mysql优化器的工作方式,使用它需要通过内部连接条件来加入这个尴尬的顺序。 即使是相当大的桌子,这也是一个快速的解决方案。如果有足够的选择性,您可能还会考虑在合并时添加索引。