简体   繁体   English

MySQL-帮助我优化此查询(改进的问题)

[英]Mysql - help me optimize this query (improved question)

About the system: - There are tutors who create classes and packs - A tags based search approach is being followed.Tag relations are created when new tutors register and when tutors create packs (this makes tutors and packs searcheable). 关于系统: -有辅导老师创建班级和课程包-遵循基于标签的搜索方法。当新辅导老师注册并在辅导老师创建课程包时会创建标签关系(这使辅导老师和课程包可搜索)。 For details please check the section How tags work in this system ? 有关详细信息,请检查标签在此系统中如何工作 below. 下面。

Following is the concerned query 以下是相关查询

SELECT SUM(DISTINCT( t.tag LIKE "%Dictatorship%" )) AS key_1_total_matches,
       SUM(DISTINCT( t.tag LIKE "%democracy%" ))    AS key_2_total_matches,
       COUNT(DISTINCT( od.id_od ))                  AS tutor_popularity,
       CASE
         WHEN ( IF(( wc.id_wc > 0 ), ( wc.wc_api_status = 1
                                       AND wc.wc_type = 0
                                       AND wc.class_date > '2010-06-01 22:00:56'
                                       AND wccp.status = 1
                                       AND ( wccp.country_code = 'IE'
                                              OR wccp.country_code IN ( 'INT' )
                                           ) ), 0)
              ) THEN 1
         ELSE 0
       END                                          AS 'classes_published',
       CASE
         WHEN ( IF(( lp.id_lp > 0 ), ( lp.id_status = 1
                                       AND lp.published = 1
                                       AND lpcp.status = 1
                                       AND ( lpcp.country_code = 'IE'
                                              OR lpcp.country_code IN ( 'INT' )
                                           ) ), 0)
              ) THEN 1
         ELSE 0
       END                                          AS 'packs_published',
       td . *,
       u . *
FROM   tutor_details AS td
       JOIN users AS u
         ON u.id_user = td.id_user
       LEFT JOIN learning_packs_tag_relations AS lptagrels
         ON td.id_tutor = lptagrels.id_tutor
       LEFT JOIN learning_packs AS lp
         ON lptagrels.id_lp = lp.id_lp
       LEFT JOIN learning_packs_categories AS lpc
         ON lpc.id_lp_cat = lp.id_lp_cat
       LEFT JOIN learning_packs_categories AS lpcp
         ON lpcp.id_lp_cat = lpc.id_parent
       LEFT JOIN learning_pack_content AS lpct
         ON ( lp.id_lp = lpct.id_lp )
       LEFT JOIN webclasses_tag_relations AS wtagrels
         ON td.id_tutor = wtagrels.id_tutor
       LEFT JOIN webclasses AS wc
         ON wtagrels.id_wc = wc.id_wc
       LEFT JOIN learning_packs_categories AS wcc
         ON wcc.id_lp_cat = wc.id_wp_cat
       LEFT JOIN learning_packs_categories AS wccp
         ON wccp.id_lp_cat = wcc.id_parent
       LEFT JOIN order_details AS od
         ON td.id_tutor = od.id_author
       LEFT JOIN orders AS o
         ON od.id_order = o.id_order
       LEFT JOIN tutors_tag_relations AS ttagrels
         ON td.id_tutor = ttagrels.id_tutor
       JOIN tags AS t
         ON ( t.id_tag = ttagrels.id_tag )
             OR ( t.id_tag = lptagrels.id_tag )
             OR ( t.id_tag = wtagrels.id_tag )
WHERE  ( u.country = 'IE'
          OR u.country IN ( 'INT' ) )
       AND CASE
             WHEN ( ( t.id_tag = lptagrels.id_tag )
                    AND ( lp.id_lp > 0 ) ) THEN lp.id_status = 1
                                                AND lp.published = 1
                                                AND lpcp.status = 1
                                                AND ( lpcp.country_code = 'IE'
                                                       OR lpcp.country_code IN (
                                                          'INT'
                                                          ) )
             ELSE 1
           END
       AND CASE
             WHEN ( ( t.id_tag = wtagrels.id_tag )
                    AND ( wc.id_wc > 0 ) ) THEN wc.wc_api_status = 1
                                                AND wc.wc_type = 0
                                                AND
             wc.class_date > '2010-06-01 22:00:56'
                                                AND wccp.status = 1
                                                AND ( wccp.country_code = 'IE'
                                                       OR wccp.country_code IN (
                                                          'INT'
                                                          ) )
             ELSE 1
           END
       AND CASE
             WHEN ( od.id_od > 0 ) THEN od.id_author = td.id_tutor
                                        AND o.order_status = 'paid'
                                        AND CASE
             WHEN ( od.id_wc > 0 ) THEN od.can_attend_class = 1
             ELSE 1
                                            END
             ELSE 1
           END
       AND ( t.tag LIKE "%Dictatorship%"
              OR t.tag LIKE "%Democracy%" )
GROUP  BY td.id_tutor
HAVING key_1_total_matches = 1
       AND key_2_total_matches = 1
ORDER  BY tutor_popularity DESC,
          u.surname ASC,
          u.name ASC
LIMIT  0, 20  

The problem 问题

The results returned by the above query are correct (AND logic working as per expectation), but the time taken by the query rises alarmingly for heavier data and for the current data I have it is like 25 seconds as against normal query timings of the order of 0.005 - 0.0002 seconds, which makes it totally unusable. 以上查询返回的结果是正确的(AND逻辑按预期工作),但是对于较重的数据,查询所花费的时间令人震惊地增加,对于我拥有的当前数据,该查询的时间大约为25秒(与订单的正常查询时间相比) 0.005-0.0002秒,这使其完全无法使用。

It is possible that some of the delay is being caused because all the possible fields have not yet been indexed. 由于尚未索引所有可能的字段,因此可能导致某些延迟。 The tag field of tags table is indexed. 标签表的标签字段已建立索引。 Is there something faulty with the query? 查询有问题吗? What can be the reason behind 20+ seconds of execution time? 超过20秒的执行时间会是什么原因?

How tags work in this system? 标签在此系统中如何工作?

  • When a tutor registers, tags are entered and tag relations are created with respect to tutor's details like name, surname etc. 当教师注册时,将输入标签并根据教师的详细信息(例如姓名,姓氏等)创建标签关系。
  • When a Tutors create packs, again tags are entered and tag relations are created with respect to pack's details like pack name, description etc. 当辅导员创建包时,将再次输入标签,并根据包的详细信息(如包名称,说明等)创建标签关系。
  • tag relations for tutors stored in tutors_tag_relations and those for packs stored in learning_packs_tag_relations. 存储在tutors_tag_relations中的导师的标签关系以及存储在learning_packs_tag_relations中的包的标签关系。 All individual tags are stored in tags table. 所有单个标签都存储在标签表中。

The explain query output:- Please see this screenshot - http://www.test.examvillage.com/Explain_query.jpg 说明查询输出:-请参见此屏幕截图-http: //www.test.examvillage.com/Explain_query.jpg

You may see if it helps adding indexes on following fields: 您可能会看到它是否有助于在以下字段上添加索引:

lptagrels.id_tutor
wtagrels.id_tutor
od.id_author

and then try to get rid of the case-when structures from the where clause. 然后尝试从where子句中删除case-when结构。 You can add some table specific restrictions directly to join like: left join t2 on t1.id = t2.id AND ... to make code more readable. 您可以直接添加一些特定于表的限制来left join t2 on t1.id = t2.id AND ...例如: left join t2 on t1.id = t2.id AND ...以使代码更具可读性。

EDIT: Seems you have a wrong approach here: you search for all tags and then count the tags that match search. 编辑:似乎您在这里有一个错误的方法:搜索所有标签,然后计算与搜索匹配的标签。 Instead you should search for tags that match and then count results that have these tags. 相反,您应该搜索匹配的标签,然后计算具有这些标签的结果。

I've recently had a similar problem. 我最近也遇到过类似的问题。 I had to modify a query to implement a new feature, and that meant adding several joins and left joins. 我必须修改查询以实现新功能,这意味着要添加多个联接和左联接。 The logic was correctly implemented, but it took forever with some bigger tables. 该逻辑已正确实现,但它用了一些更大的表。

The solution was a complete rewrite, as Brian suggests. 正如Brian所建议的,该解决方案是一个完整的重写。

My new approach was something like this: 我的新方法是这样的:

  • create a temporary table and insert here all relevant data that might end up in the final result set 创建一个临时表,并在此处插入所有可能会出现在最终结果集中的相关数据
  • run several updates on this table, joining the required tables one at a time instead of all of them at the same time 在此表上运行多个更新,一次连接一个所需表,而不是一次连接所有表
  • finally perform a query on this temporary table to extract the end result 最后对此临时表执行查询以提取最终结果

All this was done in a stored procedure, the end result has passed unit tests, and is blazing fast. 所有这些都是在存储过程中完成的,最终结果已经通过了单元测试,并且发展迅速。

UPDATE UPDATE

Please test this query, to see if it returns the same results as the original. 请测试此查询,以查看其返回的结果是否与原始查询相同。 If it does, then I will further try to find a better implementation. 如果可以,那么我将进一步尝试找到更好的实现。

SELECT SUM(DISTINCT( t.tag LIKE "%Dictatorship%" )) AS key_1_total_matches,
       SUM(DISTINCT( t.tag LIKE "%democracy%" ))    AS key_2_total_matches,
       COUNT(DISTINCT( od.id_od ))                  AS tutor_popularity,
        (wc.id_wc > 0 
        AND wc.wc_api_status = 1
        AND wc.wc_type = 0
        AND wc.class_date > '2010-06-01 22:00:56'
        AND wccp.status = 1
        AND ( wccp.country_code = 'IE' OR wccp.country_code IN ( 'INT' )) 
        ) AS 'classes_published',
        (lp.id_lp > 0  
        AND lp.id_status = 1
        AND lp.published = 1
        AND lpcp.status = 1
        AND ( lpcp.country_code = 'IE' OR lpcp.country_code IN ( 'INT' ) ) 
        ) AS 'packs_published',
       td . *,
       u . *
FROM   tutor_details AS td JOIN users AS u ON u.id_user = td.id_user
            LEFT JOIN order_details AS od                           ON td.id_tutor = od.id_author
                LEFT JOIN orders AS o                               ON od.id_order = o.id_order
            LEFT JOIN learning_packs_tag_relations AS lptagrels     ON td.id_tutor = lptagrels.id_tutor -- 
               LEFT JOIN learning_packs AS lp                       ON lptagrels.id_lp = lp.id_lp
                   LEFT JOIN learning_packs_categories AS lpc       ON lpc.id_lp_cat = lp.id_lp_cat
                       LEFT JOIN learning_packs_categories AS lpcp  ON lpcp.id_lp_cat = lpc.id_parent
                   LEFT JOIN learning_pack_content AS lpct          ON ( lp.id_lp = lpct.id_lp )
            LEFT JOIN webclasses_tag_relations AS wtagrels          ON td.id_tutor = wtagrels.id_tutor  -- 
               LEFT JOIN webclasses AS wc                           ON wtagrels.id_wc = wc.id_wc
                   LEFT JOIN learning_packs_categories AS wcc       ON wcc.id_lp_cat = wc.id_wp_cat
                       LEFT JOIN learning_packs_categories AS wccp  ON wccp.id_lp_cat = wcc.id_parent
            LEFT JOIN tutors_tag_relations AS ttagrels              ON td.id_tutor = ttagrels.id_tutor -- 
        JOIN tags AS t                                              ON ( t.id_tag = ttagrels.id_tag ) 
                                                                        OR ( t.id_tag = lptagrels.id_tag ) 
                                                                        OR ( t.id_tag = wtagrels.id_tag )
WHERE  ( u.country = 'IE' OR u.country IN ( 'INT' ) )
       AND (NOT ( t.id_tag = lptagrels.id_tag AND lp.id_lp > 0) 
            or (lp.id_status = 1
                AND lp.published = 1
                AND lpcp.status = 1
                AND ( lpcp.country_code = 'IE' OR lpcp.country_code IN ('INT') )
                ) 
            )
       AND (not (t.id_tag = wtagrels.id_tag AND wc.id_wc > 0 )
            or (
                wc.wc_api_status = 1
                AND wc.wc_type = 0
                AND wc.class_date > '2010-06-01 22:00:56'
                AND wccp.status = 1
                AND ( wccp.country_code = 'IE' OR wccp.country_code IN ('INT' ) )
                )
            )
       AND (NOT (od.id_od > 0) 
            OR (
                od.id_author = td.id_tutor
                AND o.order_status = 'paid'
                AND (NOT (od.id_wc > 0) OR od.can_attend_class = 1)
                )
        )
       AND ( t.tag LIKE "%Dictatorship%" OR t.tag LIKE "%Democracy%" )
GROUP  BY td.id_tutor
HAVING key_1_total_matches = 1
       AND key_2_total_matches = 1
ORDER  BY tutor_popularity DESC,
          u.surname ASC,
          u.name ASC
LIMIT  0, 20  

Take this code out the back and shoot it. 将此代码取出来拍摄。

Then start again. 然后重新开始。

I'm not being flippant but this is horrific and you would do yourself and anyone else touching it in the future a big favour by getting rid of it right now. 我不是在轻描淡写,但是这太可怕了,您将自己和将来的任何其他人触摸它,现在就摆脱它会成为一个大忙。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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