SQL Query between 3-tables of a many-to-many relat

2019-06-27 11:15发布

问题:

I have three tables: friends, locations, friend_location

friend_location is a join table to allow the many-to-many relationship between friends and locations, so the tables will look something like this:

Friends


ID  | Name
1   | Jerry
2   | Nelson
3   | Paul

location


ID  | Date       | Lat   | Lon 
1   | 2012-03-01 | 34.3  |  67.3
2   | 2011-04-03 | 45.3  |  49.3
3   | 2012-05-03 | 32.2  |  107.2

friend_location


Friend_ID  | Location_id
1          |  2
2          |  1
3          |  3
2          |  2

What I would like to do is get the latest location for each friend.

Results


ID  | Friend | Last Know Location  | last know date
1   | Jerry  |  45.3 , 49.3        | 2011-04-03
2   | Nelson |  34.3 , 67.3        | 2012-03-01
3   | Paul   |  32.2 , 107.2       | 2012-05-03

This is what I have tried after looking at various examples, but it returns to many results and is not correct:


    select f.id , f.name , last_known_date
    from friends f, (

    select distinct fl.friend_id as friend_id, fl.location_id as location_id, m.date as last_known_date
    from friend_location fl

    inner join (
        select location.id as id, max(date) as date
        from location
        group by location.id
    ) m

    on fl.location_id=m.id

    ) as y
    where f.id=y.friend_id

Any suggestions would be greatly appreciated.

回答1:

You could do something like this:

SELECT  f.id, f.name, last_known_date, l.Lat, L.Lon
from Friends f
join
(
    select  f.id, MAX(l.Date) as last_known_date
    from    Friends f
    JOIN    Friend_Location fl on f.ID = fl.Friend_ID
    JOIN    Location l on l.ID = fl.Location_ID
    GROUP BY f.id
) FLMax
on FLMax.id = f.id
join Friend_Location fl on fl.friend_ID = f.ID
join Location l on fl.location_ID = l.ID AND l.Date = FLMax.Last_Known_Date

Basically your problem is that you are grouping by location.id which will give you all of the locations because the ID is unique.

This only works if a friend can only be at 1 location at any 1 time.



回答2:

Gregs query looks correct to me, I came up with similar one (see below). However current db schema doesn't handle situation when two friends visited same locations in different order. For me it seems that Date column should be in friend_location table, not location. However if it's not then query is:

SELECT F.ID, F.Name AS Friend, L.Lat, L.Lon, L.Date
FROM
(
    SELECT MAX(L.date) AS max_date, F.ID
    FROM Friends F 
    JOIN friend_location FL ON F.ID=FL.Friend_ID
    JOIN Location L ON L.ID=FL.Location_id
    GROUP BY F.ID
) AS X
JOIN Friends F ON X.ID=F.ID
JOIN friend_location FL ON F.ID=FL.Friend_ID
JOIN location L ON L.ID=FL.Location_id AND L.Date=X.max_date


回答3:

Your data layout is a bit strange because the date is in the location table. So, the following retrieves the latest date for each friend:

select fl.friend_id, max(l.date) as maxdate
from friend_location fl
     location l join
     on fl.location_id = l.location_id

Now, let's just join back in the information to this query:

select f.*, maxdate, l.*
from (select fl.friend_id, max(l.date) as maxdate
      from friend_location fl
      JOIN location l
           on fl.location_id = l.id
      group by fl.friend_id
     ) flmax join
     friends f
       on flmax.friend_id = f.id
     join location l
       on l.date = flmax.maxdate

This will work, assuming that the locations don't have duplicate dates. If they do, the query is a bit more complicated. Can we make this assumption?



回答4:

You can use:

SELECT 
    a.*,
    CONCAT(d.Lat, ' , ', d.Lon) AS last_known_location,
    d.Date AS last_known_date
FROM 
    friends a
JOIN
(
    SELECT   a.Friend_ID, MAX(b.Date) AS maxdate
    FROM     friend_location a
    JOIN     location b ON a.Location_id = b.ID
    GROUP BY a.Friend_ID
) b ON a.ID = b.Friend_ID
JOIN
    friend_location c ON b.Friend_ID = c.Friend_ID
JOIN
    location d ON c.Location_id = d.ID AND b.maxdate = d.Date