简体   繁体   中英

speed up mysql query with multiple subqueries

I'm wondering if there's a way to speed up a mysql query which is ordered by multiple subqueries.

On a music related site users can like different things like artists, songs, albums etc. These "likes" are all stored in the same table. Now I want to show a list of artists ordered by the number of "likes" by the users friends and all users. I want to show all artists, also those who have no likes at all.

I have the following query:

SELECT `artists`.*, 

    // friend likes
    (SELECT COUNT(*)
     FROM `likes`
     WHERE like_type = 'artist'
     AND like_id = artists.id
     AND user_id IN (1,2,3,4, etc) // ids of friends
     GROUP BY like_id
    ) AS `friend_likes`, 

    // all likes
    (SELECT COUNT(*)
     FROM `likes`
     WHERE like_type = 'artist'
     AND like_id = artists.id
     GROUP BY like_id
    ) AS `all_likes`

FROM artists
ORDER BY 
    friend_likes DESC, 
    all_likes DESC, 
    artists.name ASC

The query takes ± 1.5 seconds on an artist table with 2000 rows. I'm afraid that this takes longer and longer as the table gets bigger and bigger. I tried using JOINS by can't seem to get this working because the subqueries contain WHERE statements.

Any ideas in the right direction would be greatly appreciated!

Try using JOIN s instead of subqueries:

SELECT
  artists.*, -- do you really need all this?
  count(user_id) AS all_likes,
  sum(user_id IN (1, 2, 3, 4)) AS friend_likes
FROM artists a
LEFT JOIN likes l
  ON l.like_type = 'artist' AND l.like_id = a.id
GROUP BY a.id
ORDER BY 
  friend_likes DESC, 
  all_likes DESC, 
  artists.name ASC;

If this doesn't make the query faster, try adding indices, or consider selecting less fields.

You need to break this down a bit to see where the time goes. You're absolutely right that 1.5 sec on 2000 rows won't scale well. I suspect you need to look at indexes and foreign-key relationships. Look at each count/group-by query individually to get them tuned as best you can then recombine.

try rolling up into a query using inline IF() and go through the table/join ONCE

SELECT STRAIGHT_JOIN
  artists.*
  , LikeCounts.AllCount
  , LikeCounts.FriendLikeCount
FROM  
  (SELECT
    like_id
    , count(*) AllCount
    , sum( If( User_id in ( 1, 2, 3, 4 ), 1, 0 ) as FriendLikeCount
  FROM
    friend_likes
  WHERE
    like_type = 'artist'
  GROUP BY 
    like_id ) LikeCounts
JOIN artists ON LikeCounts.like_id = artists.id
ORDER BY
  LikeCounts.FriendLikeCount DESC
  , LikeCounts.AllCount DESC
  , artists.name ASC

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