mysql - SQL查询从相关记录、子查询中获取最小值

标签 mysql sql database relational-database

我在 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;

<强> 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

编辑

你的 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

其背后的原因是您的查询在选择列表中包含列,而不是在分组依据中,因此不能保证返回的半径将是最小的。例如,如果您更改记录插入到 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 )。

MMySQL documents状态:

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/

相关文章:

java - 如何在新的 fxml javaFX0 上重定向用户

database - PGAdmin:未连接到服务器或与服务器的连接已关闭

php - 关联数组

javascript - php/mysql 动态下拉菜单的 AJAX RETURN 帮助

SQL 从 Excel VBA : Create a temporary table in VBA (for reuse) from a SQL query

mysql - 对 MySQL 中 GROUP_CONCAT 的每个元素执行 LIKE 操作

php - 不使用 sql 外键的缺点

php - 通过 PHP 将多个图像和数据上传到 MySQL 数据库的表单

php - 数据库查询表选择从第二行开始而不是第一行

sql - 如何查询前 10 行,下一次从表中查询其他 10 行