我看过这里:Selecting all corresponding fields using MAX and GROUP BY以及关于SO的类似网页,但我似乎无法让所有字段正确排列。
我觉得我正处于弄清楚这一点的风口浪尖,但也许我正朝着错误的道路前进,需要以不同的方式看待它。
我想要的是每个卧室的每个属性名称租金最低的单位,其合并标志设置为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?或者至少以某种方式将其作为派生表?
答案 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优化器的工作方式,使用它需要通过内部连接条件来加入这个尴尬的顺序。 即使是相当大的桌子,这也是一个快速的解决方案。如果有足够的选择性,您可能还会考虑在合并时添加索引。