SQL Group By and min (MySQL)

Posted

技术标签:

【中文标题】SQL Group By and min (MySQL)【英文标题】: 【发布时间】:2012-07-25 21:22:19 【问题描述】:

我有以下 SQL:

select code, distance from places;    

输出如下:

CODE    DISTANCE            LOCATION
106     386.895834130068    New York, NY
80      2116.6747774121     Washington, DC
80      2117.61925131453    Alexandria, VA
106     2563.46708627407    Charlotte, NC

我希望能够只获得一个代码和最近的距离。所以我希望它返回这个:

CODE    DISTANCE            LOCATION
106     386.895834130068    New York, NY
80      2116.6747774121     Washington, DC

我原来是这样的:

SELECT code, min(distance), location
GROUP BY code
HAVING distance > 0 
ORDER BY distance ASC

如果我不想获得与最短距离相关的正确位置,则该最小值可以正常工作。我如何获得最小(距离)和正确的位置(取决于表中插入的顺序,有时您最终可能会得到纽约距离,但位置中的夏洛特)。

【问题讨论】:

提前告诉 DBMS 会很好...... 克里斯,为什么你对每个答案下的表现如此好奇?您不会执行一次建议的查询并缓冲结果以获得简单的code 1:1 closest location 关系吗?就我而言,代码和位置之间的距离不会经常变化...... 【参考方案1】:

您可以尝试在最小分组和原始表之间进行嵌套查找。

这似乎可以解决问题

SELECT MinPlaces.Code, MinPlaces.Distance, Places.Location 
FROM Places INNER JOIN
(
    SELECT Code, MIN(Distance) AS Distance
    FROM Places
    GROUP BY Code
    HAVING MIN(Distance) > 0 
) AS MinPlaces ON Places.Code = MinPlaces.Code AND Places.Distance = MinPlaces.Distance
ORDER BY MinPlaces.Distance ASC

更新:使用以下测试:

DECLARE @Places TABLE ( Code INT, Distance FLOAT, Location VARCHAR(50) )

INSERT INTO @Places (Code, Distance, Location)
VALUES
(106, 386.895834130068, 'New York, NY'),
(80, 2116.6747774121, 'Washington, DC'),
(80, 2117.61925131453, 'Alexandria, VA'),
(106, 2563.46708627407, 'Charlotte, NC')

SELECT MinPlaces.Code, MinPlaces.Distance, P.Location 
FROM @Places P INNER JOIN
(
    SELECT Code, MIN(Distance) AS Distance
    FROM @Places
    GROUP BY Code
    HAVING MIN(Distance) > 0 
) AS MinPlaces ON P.Code = MinPlaces.Code AND P.Distance = MinPlaces.Distance
ORDER BY MinPlaces.Distance ASC

这会产生:

【讨论】:

@ErikE:更新了我的答案。我也喜欢使用 CTE。 你仍然是自加入的地方,这将比序列项目的性能更差......【参考方案2】:

您没有说您的 DBMS。以下解决方案适用于 SQL Server。

WITH D AS (
   SELECT code, distance, location,
      Row_Number() OVER (PARTITION BY code ORDER BY distance) Seq
   FROM places
)
SELECT *
FROM D
WHERE Seq = 1

如果您有一个包含唯一代码的表,并且在 [代码、距离] 上的 Places 表中有一个索引,那么 CROSS APPLY 解决方案可能会更好:

SELECT
   X.*
FROM
   Codes C
   CROSS APPLY (
      SELECT TOP 1 *
      FROM Places P
      WHERE C.Code = P.Code
      ORDER BY P.Distance
   ) X

直到很久以后,我才能为 mysql 提供解决方案。

附:您不能依赖广告订单。不要尝试!

【讨论】:

唯一代码是什么意思,因为我提供的样本有重复代码 如果您有一个单独的表格列出所有代码,每个代码 1 行! 是的,我知道我不能依赖广告订单。无论如何,我的代码实际上是一个 user.id,它来自用户表并链接到具有距离和位置的位置表 那么是的,它们是唯一的,并且它们与位置表具有一对多的关系 它在 mysql 中,性能是否很快,因为我将拥有 100,000 个位置【参考方案3】:

要获得正确的关联位置,您需要加入一个子选择,该子选择在外部主表中的距离与子选择中派生的最小距离匹配的条件下获得每个代码的最小距离。

SELECT a.code, a.distance
FROM   places a
INNER JOIN
(
    SELECT   code, MIN(distance) AS mindistance
    FROM     places
    GROUP BY code
) b ON a.code = b.code AND a.distance = b.mindistance
ORDER BY a.distance

【讨论】:

这对于大约 100,000 个位置的性能如何? @chris,因为您使用的是 MySQL,所以这可能是您能找到的最有效的解决方案。您必须确保在 codedistance 字段上设置了正确的索引。 是的,代码是 PK,所以没关系,但距离是用数学计算的(使用 lat 和 long 以及谷歌的地图 api) @chris,如果性能无法满足您的需求,您可能需要考虑使用spatial database 而不是关系型。关系数据库只能让您了解这些类型的查询,但空间数据库更适合它们。 使用“边界框”得到更短的候选列表,然后使用距离函数。这可以让您排除 100,000 中的大部分。

以上是关于SQL Group By and min (MySQL)的主要内容,如果未能解决你的问题,请参考以下文章

SQL 10位时间戳 除以60 得到整分钟;group by event_ts/60<==> group by 1min

SQL 10位时间戳 除以60 得到整分钟;group by event_ts/60<==> group by 1min

SQL Server: Difference between PARTITION BY and GROUP BY

带有 SQL MIN() 和 GROUP BY 的额外字段

如何将此 GROUP BY / MIN SQL 查询转换为 LINQ?

SQL Select min(date) group by 对应的行