How to convert dependent subquery to join for better performance?

不问归期 提交于 2020-01-02 07:43:24

问题


I have a database that stores "themes" and every theme is associated with a whole bunch of images (=screenshots of these themes). Now I want to display the latest 10 themes and for every theme I only want to get one single image from the database (the one with the lowest ID).

Currently my query looks like this (I am using a subquery):

SELECT DISTINCT 
  t.theme_id, t.theme_name, theme_date_last_modification, image_id, image_type

FROM 
  themes t, theme_images i

WHERE 
  i.theme_id = t.theme_id
  AND t.theme_status = 3
  AND t.theme_date_added < now( )
  AND i.image_id = (
    SELECT MIN( image_id )
    FROM theme_images ii
    WHERE ii.theme_id = t.theme_id 
  )

GROUP BY 
  t.theme_id

ORDER BY 
  t.theme_date_last_modification DESC

LIMIT 10

It works, but the query is very slow. When I use EXPLAIN I can see that there's a "dependent subquery". Is it possible to convert this dependent subquery into some kind of join that can be processed faster by mysql?

P.S.: My actual query is much more complex and makes use of more tables. I have already tried to simplify it as much as possible so that you can concentrate on the actual reason for the performance-problems.

EDIT: This is the output of EXPLAIN:

id  select_type         table   type    possible_keys              key       key_len   ref                 rows  Extra   
1   PRIMARY             t       index   PRIMARY,themes             themes    212       NULL                5846  Using where; Using index; Using temporary; Using filesort
1   PRIMARY             i       eq_ref  PRIMARY,theme_id,image_id  PRIMARY   4         func                1     Using where
2   DEPENDENT SUBQUERY  ii      ref     theme_id                   theme_id  4         themes.t.theme_id   6   

回答1:


Try this query firstly -

SELECT
  t.*, ti1.*
FROM
  themes t
JOIN theme_images ti1
  ON ti1.theme_id = t.theme_id
JOIN (SELECT theme_id, MIN(image_id) image_id FROM theme_images GROUP BY theme_id) ti2
  ON ti1.theme_id = ti2.theme_id AND ti1.image_id = ti2.image_id
ORDER BY 
  t.theme_date_last_modification DESC
LIMIT 10

One more solution -

SELECT
  t.*, ti.*
FROM
  themes t
JOIN (SELECT * FROM theme_images ORDER BY image_id) ti
  ON ti.theme_id = t.theme_id
GROUP BY
  theme_id 
ORDER BY 
  t.theme_date_last_modification DESC
LIMIT
  10

Then add your WHERE filter.




回答2:


One approach is to first LIMIT on the themes table, then JOIN to images:

SELECT
    t.theme_id, t.theme_name, t.theme_date_last_modification, 
    ti.image_id, ti.image_type
FROM
      ( SELECT theme_id, theme_name, theme_date_last_modification
        FROM themes t
        WHERE theme_status = 3
          AND theme_date_added < now( )
        ORDER BY 
          theme_date_last_modification DESC
        LIMIT 10 
      ) AS t
    JOIN                     -- LEFT JOIN if you want themes without an image 
        theme_images AS ti   -- to be shown
          ON ti.theme_id = t.theme_id
         AND ti.image_id =
             ( SELECT ii.image_id
               FROM theme_images AS ii
               WHERE ii.theme_id = t.theme_id
               ORDER BY ii.image_id
               LIMIT 1
             ) 

ORDER BY 
    t.theme_date_last_modification DESC ;

With an index on themes (theme_status, theme_date_last_modification, theme_id, theme_date_added) the limit subquery should be efficient.

I suppose you also have a (unique) index on theme_images (theme_id, image_id).



来源:https://stackoverflow.com/questions/18762405/how-to-convert-dependent-subquery-to-join-for-better-performance

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!