MYSQL JOIN和GROUP / DISTINCT

时间:2013-08-25 15:38:26

标签: php mysql database join

我有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中循环结果并重建它们;但是这个查询很容易返回数千个结果,因为结果中显示了每个用户的位置,而我宁愿不为速度目的而这样做。

非常感谢任何正确方向的帮助。

ADDITION

详细说明结果问题,如果您在ORLANDO上运行此查询,其半径将扩展到DAYTONA,如果用户在DAYTONA中,您将获得

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

导致用户1和1的重复。 3

但是当你按user.id分组时,你只能获得

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

删除用户1&amp; 3因为当它分组时它只显示它们的区域为DAYTONA

1 个答案:

答案 0 :(得分:0)

如果您使用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`

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

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}))之类的部分而不是动态执行它会更有效率,保留这样的理由是什么原因?
  • 另外,您可能希望以弧度存储长/纬度值以进一步优化