我在 mysql 数据库中有以下表:
mechanics: id, name, distance
mechanic_zones: mechanic_id, zone, radius
机械师表记录了机械师、ID、姓名以及他们与工作的距离(这实际上是使用机械师的邮政编码和工作的邮政编码计算的,但为了清楚起见,我对此进行了简化)
mechanic_zones 表允许机械师定义其区域的半径,并用于为工作定价
mechanics:
1, Jon, 5.4
2, Paul, 6.5
3, George, 20
mechanic_zones:
1, a, 5
1, b, 10
1, c, 20
2, a, 10
2, b, 20
2, c, 50
3, a, 5
3, b, 10
3, c, 15
Jon 将他的区域定义为:a - 5 英里、b - 10 英里和 c - 20 英里。
Paul 的区域定义为:a - 10 英里、b - 20 英里和 c - 50 英里。
乔治的区域定义为:a - 5 英里、b - 10 英里和 c - 15 英里。
我希望能够找到机械师工作的最低区域。 在示例中,乔恩距离工作地点 5.4 英里,保罗距离工作地点 6.5 英里,乔治距离工作地点 20 英里。
因此查询应返回类似以下内容的内容:
mechanic_id, name, zone, distance
2, Paul, A, 6.5
1, Jon, B, 5.4
这项工作位于 Paul 的 A 区,因为 6.5 英里处位于他定义为 A 区的 10 英里范围内。
它位于 Jon 的 B 区,因为它大于他的 5 英里 A 区,但小于他的 B 区 10 英里限制。
对于乔治来说,这里超出了他的区域,因为它比他的 20 英里 C 区还要大。
据我所知:
SELECT id, name, (distance * 1) as distance_to_job, min(mz.`zone`) as min_zone, min(mz.radius) as min_radius, max(mz.`zone`) as max_zone, max(mz.radius) as max_radius
FROM mechanics m, mechanic_zones mz
WHERE m.id = mz.mechanic_id
GROUP BY m.id, postcode
HAVING distance_to_job < max_radius
ORDER BY distance_to_job ASC, radius ASC
(我认为)这给了我区域内的所有机制,但实际上并没有弄清楚距离位于哪个区域。
非常感谢任何帮助
最佳答案
您需要使用额外的子查询来找出每个机械师的最小半径(其中半径大于距离),然后您可以将其连接回两个表并获取您需要的所有列信息从两个表中:
SELECT m.ID, mz.Zone, m.distance, mz.radius
FROM Mechanics m
INNER JOIN mechanic_zones mz
ON mz.Mechanic_ID = m.ID
INNER JOIN
( SELECT m.ID,
MIN(mz.radius) AS radius
FROM Mechanics m
INNER JOIN mechanic_zones mz
ON mz.Mechanic_ID = m.ID
WHERE mz.radius > M.distance
GROUP BY m.ID
) MinZone
ON MinZone.ID = m.ID
AND MinZone.radius= mz.radius
ORDER BY mz.Zone;
如果您实际上不想知道所选区域的半径,并且半径最小的区域将始终具有最小的字母,您可以使用:
SELECT m.ID, mz.MinZone, m.distance
FROM Mechanics m
INNER JOIN
( SELECT m.ID,
MIN(mz.Zone) AS Zone
FROM Mechanics m
INNER JOIN mechanic_zones mz
ON mz.Mechanic_ID = m.ID
WHERE mz.radius > M.distance
GROUP BY m.ID
) MinZone
ON MinZone.ID = m.ID
ORDER BY MinZone.Zone;
编辑
你的 fiddle 与我使用的非常接近,但我会使用以下内容,以便计算只完成一次:
SELECT m.id, m.name, m.distance, m.radius, m.zone
FROM ( SELECT m.ID,
m.Name,
m.Distance,
MIN(mz.radius) AS radius
FROM ( SELECT ID, Name, (1 * Distance) AS Distance
FROM Mechanics
) m
INNER JOIN mechanic_zones mz
ON mz.Mechanic_ID = m.ID
WHERE mz.radius > M.distance
GROUP BY m.ID, m.Name, m.Distance
) m
INNER JOIN mechanic_zones mz
ON mz.Mechanic_ID = m.ID
AND mz.radius = m.radius;
其背后的原因是您的查询在选择列表中包含列,而不是在分组依据中,因此不能保证返回的半径将是最小的。例如,如果您更改记录插入到 mechanic_zones ( as in this fiddle ) 的顺序,结果将变为:
ID NAME DTJ RADIUS ZONE
1 Jon 2 10 a
2 Paul 11 50 b
3 George 5 5 a
而不是
ID NAME DTJ RADIUS ZONE
1 Jon 2 5 a
2 Paul 11 20 b
3 George 5 5 a
如您所见,乔恩的半径是错误的。为了进一步解释这一点,下面摘录了我写的关于 MySQL 实现隐式分组的缺点的解释。
<小时/>我建议尽可能避免 MySQL 提供的隐式分组,我的意思是在选择列表中包含列,即使它们不包含在聚合函数或 group by 子句中。
想象一下下面的简单表格 (T):
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
2 | A | Y |
在 MySQL 中你可以这样写
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1;
这实际上违反了 SQL 标准,但它在 MySQL 中可以工作,但问题是它是不确定的,结果:
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
不比
正确或不正确ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
所以你的意思是为 Column1
的每个不同值提供一行,这两个结果集都满足,那么你怎么知道你会得到哪一个呢?好吧,您不知道,这似乎是一个相当流行的误解,您可以添加 ORDER BY 子句来影响结果,例如以下查询:
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1
ORDER BY ID DESC;
将确保您得到以下结果:
ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
因为ORDER BY ID DESC
,但这不是真的( as demonstrated here )。
The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause.
因此,即使您有这样的订单,直到选择每组一行之后才适用,并且这一行是非确定性的。
SQL 标准确实允许选择列表中不包含在 GROUP BY 或聚合函数中的列,但是这些列在功能上必须依赖于 GROUP BY 中的列。例如,示例表中的 ID 是 PRIMARY KEY,因此我们知道它在表中是唯一的,因此以下查询符合 SQL 标准,并且会在 MySQL 中运行,但在当前许多 DBMS 中失败(在撰写 Postgresql 时)是我所知道的最接近正确实现该标准的 DBMS):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
由于每一行的 ID 都是唯一的,因此每个 ID 只能有一个 Column1
值和一个 Column2
值,因此对于返回的内容没有任何歧义每行。
关于mysql - SQL查询从相关记录、子查询中获取最小值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17325149/