【问题标题】:MYSQL JOIN and GROUP / DISTINCTMYSQL JOIN 和 GROUP / DISTINCT
【发布时间】:2013-08-28 03:35:57
【问题描述】:

我将 3 个表连接在一起,以了解特定区域的用户。表格的缩小示例:

USER Table (stores all user information) 
ID | Name
----------
 1   John
 2   Joe
 3   Mike 

GEO (has all geo location info; including latitude and longitude; which im excluding for the example )
ID | CITY 
-------------
 1 | ORLANDO
 2 | MIAMI
 3 | DAYTONA

LOCATIONS (stores each users location; each user has multiple locations)
ID | AREA (id = user.id, geo = geo.id)
--------
 1 | 1
 1 | 2
 1 | 3
 2 | 1
 3 | 1
 3 | 3

我在 php 中创建了一个函数来提取给定 LAT / LONG 的结果,具有一定的半径(不包括整个函数,因为它不相关):

select USER.ID as USERID, (6371 * acos(cos(radians( {$lat})) * cos(radians(g.latitude)) * cos(radians(g.longitude) - radians({$long})) + sin(radians({$lat})) * sin(radians(g.latitude)))) AS distance
            from 
            GEO G 
            join LOCATIONS LOC on LOC.AREA = G.ID
            join USER U on LOC.ID = USERID
            HAVING distance <= {$radius}

现在的问题。这有效并提取了所有信息,但由于用户多次出现在 LOCATIONS 表中,因此导致多次显示同一用户(即显示 100 个结果,有 15 个不同的用户)

所以我的想法是 GROUP BY USER.id;但是,这仅匹配该用户的第一个位置;只产生 2 个结果。

我尝试过 DISTINCT;但行并不不同,因为 user.id 或 location.id 是每行的不同组合。

我也尝试过使用子查询向后工作

SELECT * from USER where id = (
select id from GEO where area = (
select id, (long trig here) as distance) from GEO)

但这不起作用,因为我必须选择 trig 语句作为距离,所以我不能只从 GEO 表中选择 id

我正在竭尽全力尝试获取唯一身份用户;但仍然让它在所有用户位置搜索。我知道我可以在 php 中循环结果并重建它们;然而,这个查询很容易返回数千个结果,因为每个用户的位置都显示在结果中,出于速度的目的,我宁愿不这样做。

任何正确方向的帮助将不胜感激..

添加

为了详细说明结果问题,如果您在 ORLANDO 上运行此查询,其半径将延伸到 DAYTONA,如果用户在 DAYTONA,您会得到

USER | CITY
-----------
 1  | ORLAND
 1  | DAYTONA
 2  | ORLANDO
 3  | ORLANDO
 3  | DAYTONA

这会导致用户 1 和 3 重复

但是当你按 user.id 分组时,你只会得到 ​​p>

 USER | CITY
-----------
 2  | ORLANDO

它会丢弃用户 1 和 3,因为它在分组时只将他们的区域显示为 DAYTONA

【问题讨论】:

    标签: php mysql database join


    【解决方案1】:

    如果您使用WHERE 而不是HAVING,您能够使用GROUP BY / DISTINCT 并且 就像这样:

    SELECT u.id AS USERID
        FROM `GEO` g
        JOIN `LOCATIONS` l ON l.`AREA` = g.`ID`
        JOIN `USER` u ON l.`ID` = u.`ID`
        WHERE (6371 * ACOS(COS(RADIANS({$lat})) * COS(RADIANS(g.latitude)) * COS(RADIANS(g.longitude) - RADIANS({$long})) + SIN(RADIANS({$lat})) * SIN(RADIANS(g.latitude)))) <= {$radius}
        GROUP BY u.`ID`
    

    这可以通过使用“早期”预聚合过滤器进行优化。 IE。尽早在ON 上应用WHERE。虽然这可能看起来很“奇怪”,但它可以明显更快。在你的情况下,这看起来像这样:

    SELECT u.id AS USERID
        FROM `GEO` g
        JOIN `LOCATIONS` l ON 
            (6371 * ACOS(COS(RADIANS({$lat})) * COS(RADIANS(g.latitude)) * COS(RADIANS(g.longitude) - RADIANS({$long})) + SIN(RADIANS({$lat})) * SIN(RADIANS(g.latitude)))) <= {$radius}
            AND l.`AREA` = g.`ID`
        JOIN `USER` u ON l.`ID` = u.`ID`        
        GROUP BY u.`ID`
    
    • 请注意,如果您还想选择距离,您仍然可以像以前一样输入选择字段列表;但是,如果使用 DISTINCT,您只会得到一个,而如果使用 GROUP BY,您将能够连接所有距离
    • 我建议同时尝试GROUP BY DISTINCT,因为性能差异可能非常极端且不可预测。 (参见例如this question
    • 只是想知道,但预先计算诸如 ACOS(COS(RADIANS({$lat})) 之类的部分会比即时计算更有效,有什么理由保持这样吗?
    • 此外,您可能希望将 long/lat 值存储为弧度以进行进一步优化

    【讨论】:

    • 你我的朋友真了不起。 MAX 不断踢出对 group by 的无效使用;所以我删除了 MAX 并选择了 DISTINCT(u.id) ,它似乎正在工作!至于预先计算,您是否建议先在我的 php 函数中执行此操作然后通过?你认为这会减少搜索时间吗? TBH 那部分是我在另一篇文章中找到的,因为 RADIUS 的搜索超出了我的想象。位置表来自美国邮政编码数据库,该数据库具有普通的纬度/经度。我可以用弧度重建它,因此不必在查询中完成;每一秒都有帮助。
    • 确切地说,一个简单的“重建”以便数据库中的 lon 和 lat 以弧度存储会加快它(一点)并使查询更简单。此外,具有ACOS(COS(RADIANS({$lat})) 等变量的部分 - 在 yuur 查询期间 - 是一个常量,因此您可以使用 php 预先计算它并使用该结果,例如$pc_lat = acos(cos(deg2rad($lat)));.
    • 关于 MAX 的问题,您说的很对,这是我的疏忽,但是,就我所见:您不需要它,因为单个值的 MAX 始终是价值。我会相应地更新上述内容
    • 在注意到您的修订之前发表了评论。与我上面的评论一样,我仍然收到 MAX 错误;但是删除 MAX 和 GROUP BY 并用 DISTINCT(u.id) 替换是可行的……只是出于好奇,知道为什么 MAX 会给无效的 group by 吗?
    • 哈哈,我还在编辑,现在已经更新了。 MAX 函数本身就是一个聚合函数,它们不允许在选择字段之外。见dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多