2

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

我觉得我正处于弄清楚这一点的风口浪尖,但也许我走错了路,需要以不同的方式看待这个问题。

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

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

合并 = 1 个查询结果的所有出租单元

上面的图像是通过这个查询获得的:

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

合并 = 1 的出租单元按属性名称和卧室按最小值分组查询结果

上图是这个查询的结果:

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?或者至少以某种方式将其作为派生表?

4

4 回答 4

1

那是因为包含在其中的列group by来自不确定的行。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
于 2013-06-20T20:11:48.293 回答
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在这里

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

于 2013-06-20T21:15:13.987 回答
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)第一行的 和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
于 2013-06-20T20:01:20.950 回答
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;

工作完美..!!

于 2013-06-20T21:05:05.887 回答