我在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英里。
保罗的区域定义为: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
这项工作在保罗的A区,因为距离他的10英里内的6.5英里区域定义为A区。
它位于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
其中(我认为)给了我区内的所有机制,但实际上没有确定距离所在的区域。
任何帮助非常感谢
答案 0 :(得分:1)
你需要使用一个额外的子查询来找出每个机制的最小半径(半径大于距离),然后你可以将它连接到你的两个表并获得你需要的所有列信息来自两个表:
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;
<强> Example on SQL Fiddle 强>
如果您实际上并不想知道所选区域的半径,并且半径最小的区域将始终具有您可以使用的最低字母:
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;
<强> Example on SQL 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;
<强> Example on SQL Fiddle 强>
这背后的原因是您的查询在选择列表中有列但不在组中,因此无法保证返回的半径最小为1。例如,如果您更改记录插入到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
正如您所看到的,Jon的半径是错误的。下面进一步解释这个是我写的关于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)。
服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。
因此,即使您有一个订单,但在每个组选择了一行之后才会适用,而且这一行是不确定的。
SQL-Standard允许选择列表中的列不包含在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
值,对于每行返回的内容没有歧义。