我看过这里:使用 MAX 和 GROUP BY 选择所有相应的字段以及类似的页面,但我似乎无法让所有字段正确排列。
我觉得我正处于解决这个问题的风口浪尖,但也许我正走在错误的道路上,需要以不同的方式看待这个问题。
我想要的是合并标志设置为 1 的每个房产名称每个卧室数量租金最低的单元。
我的SQL小提琴:http://sqlfiddle.com/#!2/881c41/2
上面的图像是通过此查询获得的:
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
上图是该查询的结果:
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。
下图是我想要的结果。
::编辑1::
我是否需要创建一个包含 2 列的链接表,其中一列中包含租赁单元 ID,另一列中包含租赁单元名称 ID? 或者至少以某种方式将其作为派生表来实现?
一般来说,除非您尝试执行某种 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
这是因为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
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;
工作完美..!!
您的查询给出了错误的结果,其原因在 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优化器的工作方式,将其用于需要通过内部连接条件来连接这个尴尬的顺序。 即使对于相当大的表来说,这也是一个快速的解决方案。如果选择性足够的话,您也可以考虑在合并时添加索引。
另一个对我有用的解决方案是将 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