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

问题描述 投票:0回答:5

我看过这里:使用 MAX 和 GROUP BY 选择所有相应的字段以及类似的页面,但我似乎无法让所有字段正确排列。

我觉得我正处于解决这个问题的风口浪尖,但也许我正走在错误的道路上,需要以不同的方式看待这个问题。

我想要的是合并标志设置为 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? 或者至少以某种方式将其作为派生表来实现?

mysql sql group-by min
5个回答
1
投票

一般来说,除非您尝试执行某种 MySQL“魔法”,否则您应该始终按 SELECT 列表中的

every
非聚合、非常量列进行分组。

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

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)
以及最后一行上添加的整体
GROUP BY

SELECT MIN(ru.id) AS 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)
GROUP BY run.name, ru.rent, ru.bedrooms

1
投票

这是因为group by中包含的列

not
来自不确定的行。 MySQL 文档在这一点上非常清楚:

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

因为我刚刚在另一篇帖子上回答了这个问题,所以我建议你看看那里。

编辑:

以下是如何将

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

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;

工作完美..!!


1
投票

您的查询给出了错误的结果,其原因在 mysql group-by 扩展中解释。

您可以尝试将分组放入子查询中,然后连接回同一个表以获取您可能需要的其他隐藏列(例如 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 这里

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


0
投票

另一个对我有用的解决方案是将 MAX/MIN 列项移动到 select 语句的末尾。认为与小组处理顺序有关。

原始(错误):

SELECT DISTINCT c.firstname, c.lastname, c.email,
    (SELECT COALESCE(jtr.hr_title, 'Other') FROM job_title_relation jtr WHERE jtr.hr_title_id = c.job_title LIMIT 1) AS `job_title_name`,
    c.position,
    ...,
    **DATE(FROM_UNIXTIME(MIN(scr.ORDERID)/1000) ) AS `reg_date`**,
    ...,
    `title_list`
FROM table1 AS c 
INNER JOIN table2 scr ON c.id = scr.ID
INNER JOIN table3 AS w ON scr.SID = w.EID 
WHERE w.EID = 123456  
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14

新(有效):

SELECT DISTINCT c.firstname, c.lastname, c.email,
    (SELECT COALESCE(jtr.hr_title, 'Other') FROM job_title_relation jtr WHERE jtr.hr_title_id = c.job_title LIMIT 1) AS `job_title_name`,
    c.position,
    ...,
    `title_list`,
    **DATE(FROM_UNIXTIME(MIN(scr.ORDERID)/1000) ) AS `reg_date`**
FROM table1 AS c 
INNER JOIN table2 scr ON c.id = scr.ID
INNER JOIN table3 AS w ON scr.SID = w.EID 
WHERE w.EID = 123456  
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14
© www.soinside.com 2019 - 2024. All rights reserved.