简体   繁体   中英

Optimize sub-query selecting last record of each group

I have this query which is a dependant query and taking much execution time

SELECT
  u.id,
  u.user_name,
  ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
  ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
  (select  created  from map  where user_id = 1  order by created desc   limit 1) as LatestTime
FROM users as u
WHERE id IN(SELECT
          user1_id FROM relation
        WHERE users.id = 1)
ORDER BY id;

I tried this query in (dependant)

SELECT
  u.id,
  u.user_name,
  m.map_id,
  m.longitude,
  m.latitude,
  m.Date as created
FROM users as u
  left join (select
           map_id,
           longitude,
           latitude,
           user_id,
           max(created) as `Date`
         from map
         group by user_id) as m
    on m.user_id = u.id
WHERE id IN(SELECT
          user1_id FROM relation
        WHERE users.id = 1)
ORDER BY id;

The problem is that the first query is dependent and working fine but taking much execution time. With the second query the problem is that it is not fetching the latest created time. Now i want to optimise this query. The theme is that in subquery i am first making group then i am trying to get the last record of each group. and here is the tables structure.

users : id , user_name
map   : map_id ,  user_id ,longitude , latitude, created 
relations : id , user1_id , user2_id , relation

Where performance is needed, subqueries in the SELECT clause are indeed a pain and have to be banished :)

You can rewrite this part:

SELECT
    u.id,
    u.user_name,
    ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
    ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
    (select  created  from map  where user_id = 1  order by created desc   limit 1) as LatestTime
FROM users as u

In:

SELECT
    u.id,
    u.user_name,
    COALESCE(m1.longitude, 0) as longitude,
    COALESCE(m1.latitude, 0) as latitude
FROM users u
LEFT JOIN map m1 ON m1.user_id = u.id
LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
WHERE m2.map_id IS NULL

I wrote a short explanation of the query structure in this answer . It's a really nice trick to learn as it is more readable, subquery-less and performance wiser.

I haven't looked at the IN part yet but will if the above didn't help.

Edit1: You can extract the created date and use a MAX() instead.

SELECT
    u.id,
    u.user_name,
    COALESCE(m1.longitude, 0) as longitude,
    COALESCE(m1.latitude, 0) as latitude,
    created.LatestTime
FROM (SELECT MAX(created) FROM map WHERE user_id = 1) created
INNER JOIN users u ON TRUE
LEFT JOIN map m1 ON m1.user_id = u.id
LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
WHERE m2.map_id IS NULL

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM