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,所以这可能是您能找到的最有效的解决方案。您必须确保在code
和 distance
字段上设置了正确的索引。
是的,代码是 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