0

我在 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 英里。

我希望能够为机械师找到工作的最低区域。在示例中,Jon 距离工作地点 5.4 英里,Paul 距离工作地点 6.5 英里,George 距离工作地点 20 英里。

所以查询应该返回如下内容:

mechanic_id, name, zone, distance
2, Paul, A, 6.5
1, Jon, B, 5.4

这份工作在保罗的 A 区,因为在 6.5 英里处,它在他定义为 A 区的 10 英里内。

它在乔恩的 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

哪个(我认为)给了我区域内的所有机制,但实际上并没有弄清楚距离在哪个区域。

非常感谢任何帮助

4

1 回答 1

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;

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;

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;

SQL Fiddle 示例

这背后的原因是您的查询在选择列表中有列,而不是在分组依据中,因此不能保证返回的半径是最低的。例如,如果您更改将记录插入到 mechanic_zones 的顺序(如本小提琴中所示),您的结果将变为:

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, 然而这不是真的(如此处所示)。

MMySQL文档状态:

服务器可以从每个组中自由选择任何值,因此除非它们相同,否则选择的值是不确定的。此外,从每个组中选择值不会受到添加 ORDER BY 子句的影响。

因此,即使您有这样的订单,直到每组选择一行之后才适用,并且这一行是不确定的。

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对于每一行返回的内容没有歧义。

于 2013-06-26T16:31:02.007 回答