mysql 使用内连接、左连接和子选择优化大型SQL查询

esyap4oy  于 2023-02-11  发布在  Mysql
关注(0)|答案(1)|浏览(102)

Problem

I have a big SQL query counting a bunch of relations and doing some calculations. However, in some cases, it runs forever (very very long, taking up 99% of DB CPU).

Research

I searched through SO and found posts like SO1 , SO2 , SO3 and others. The answer seems to be using indices to speed up the query. Further I learned to use explain to get more information about my query. My problem is, that my explain results tells me, that I am already using indices of some sort. However, I must not be using them effectively. Unfortunately, my understanding of them is also very limited. See the result of explain further down.

Background information

All tables have been generated by Doctrine . All present indices have also been created by the ORM.

Tables

Here are the tables used in the query as well as their primary and foreign keys
| name | primary & foreign |
| ------------ | ------------ |
| a | id, ... |
| b | id, a_id, ... |
| c | id, b_id, ... |
| d | id, c_id, ... |
| e | id, d_id, ... |
| f | id, e_id, ... |
| g | id, e_id, ... |
| h | id, e_id, ... |
| i | id, e_id, ... |
| j | id, e_id, ... |
| k | id, e_id, ... |
| l | id, e_id, ... |
| m | id, e_id, ... |
| n | id, e_id, ... |
| o | id, e_id, ... |
| p_e | p_id, e_id |
| q | id, e_id, ... |
| r | id, ... |
| r_f | r_id, f_id |

Query

select 
    e.id, 
    e.value_1, 
    count(DISTINCT f.id) as f_count, 
    count(DISTINCT g.id) as g_count, 
    count(DISTINCT h.id) as h_count, 
    count(DISTINCT i.id) as i_count, 
    count(DISTINCT j.id) as j_count, 
    count(DISTINCT k.id) as k_count, 
    count(DISTINCT l.id) as l_count, 
    count(DISTINCT m.id) as m_count, 
    count(DISTINCT n.id) as n_count, 
    count(DISTINCT o.id) as o_count, 
    count(DISTINCT p_e.p_id) as p_count, 
    count(DISTINCT q.id) as q_count,
    m_counts.example_1_count,
    m_counts.example_2_count,
    m_counts.example_3_count,
    m_counts.example_4_count,
    m_counts.example_5_count,
    m_counts.example_6_count,
    m_counts.example_7_count,
    m_counts.example_8_count,
    m_counts.example_9_count,
    m_counts.example_10_count,
    m_counts.example_11_count,
    m_counts.example_12_count,
    m_counts.example_13_count,
    case when e.value_1 = 'example' 
      then 
          0
      else 
        case when count(DISTINCT f.id) > 0 
              then
              (
                        select round(sum((
                                select
                                case 
                                    when f_inner.value_1 = 'a1' then f_inner.value_2 * sum(r.a1)
                                    when f_inner.value_1 = 'a2' then f_inner.value_2 * sum(r.a2)
                                    when f_inner.value_1 = 'a3' then f_inner.value_2 * sum(r.a3)
                                    when f_inner.value_1 = 'a4' then f_inner.value_2 * sum(r.a4)
                                    when f_inner.value_1 = 'a5' then f_inner.value_2 * sum(r.a5)
                                    when f_inner.value_1 = 'a6' then f_inner.value_2 * sum(r.a6)
                                    when f_inner.value_1 = 'a7' then f_inner.value_2 * sum(r.a7)
                                    when f_inner.value_1 = 'a8' then f_inner.value_2 * sum(r.a8)
                                    when f_inner.value_1 = 'a9' then f_inner.value_2 * sum(r.a9)
                                    when f_inner.value_1 = 'a10' then f_inner.value_2 * sum(r.a10)
                                    when f_inner.value_1 = 'a11' then f_inner.value_2 * count(r.id)
                                    when f_inner.value_1 = 'a12' then f_inner.value_2
                                    else 0
                                end
                                from f as f_inner
                                inner join f_r as f_e on f_r.f_id = f_inner.id
                                inner join r as r on r.id = f_r.r_id
                                where f_inner.id = f_2.id
                        )), 4)
                        from e as e_inner
                        inner join f as f_2 on e_inner.id = f_2.e_id
                        where e_inner.id = e.id
                    )
          else
                  0
          end
      end as f_value_1
    from e as e
    inner join d as d on d.id = e.d_id
    inner join c as c on c.id = d.c_id
    inner join b as b on b.id = c.b_id
    inner join a as a on a.id = b.a_id
    
    left join f as f on e.id = f.e_id
    left join g as g on e.id = g.e_id
    left join h as h on e.id = h.e_id
    left join i as i on e.id = i.e_id
    left join j as j on e.id = j.e_id
    left join k as k on e.id = k.e_id
    left join l as l on e.id = l.e_id
    left join m as m on e.id = m.e_id
    left join n as n on e.id = n.e_id
    left join o as o on e.id = o.e_id
    left join p_e as p_e on e.id = p_e.e_id
    left join q as q on e.id = q.e_id
    
    left outer join (
        select e_2.id, 
            sum(case when m_2.type = 'ex1' then 1 else 0 end) as example_1_count,
            sum(case when m_2.type = 'ex2' then 1 else 0 end) as example_2_count,
            sum(case when m_2.type = 'ex3' then 1 else 0 end) as example_3_count,
            sum(case when m_2.type = 'ex4' then 1 else 0 end) as example_4_count,
            sum(case when m_2.type = 'ex5' then 1 else 0 end) as example_5_count,
            sum(case when m_2.type = 'ex6' then 1 else 0 end) as example_6_count,
            sum(case when m_2.type = 'ex7' then 1 else 0 end) as example_7_count,
            sum(case when m_2.type = 'ex8' then 1 else 0 end) as example_8_count,
            sum(case when m_2.type = 'ex9' then 1 else 0 end) as example_9_count,
            sum(case when m_2.type = 'ex10' then 1 else 0 end) as example_10_count,
            sum(case when m_2.type = 'ex11' then 1 else 0 end) as example_11_count,
            sum(case when m_2.type = 'ex12' then 1 else 0 end) as example_12_count,
            sum(case when m_2.type = 'ex13' then 1 else 0 end) as example_13_count

            from e as e_2
            left join m as m_2 on m_2.e_id = e_2.id

            group by e_2.id
    ) as m_counts on m_counts.id = e.id
    
    where a.id = 504
    
    group by e.id;

Explain Result

idselect_typetabletypepossbile_keyskeykey_leneq_refrowsfilteredExtra
1PRIMARYaconstPRIMARYPRIMARY4const1100.00Using index; Using temporary; Using filesort
1PRIMARYbrefPRIMARY,IDX_548D5BBD166D1F9CIDX_548D5BBD166D1F9C5const10100.00Using index
1PRIMARYcrefPRIMARY,IDX_D09734DB23BCD4D0IDX_D09734DB23BCD4D05db.t.id7100.00Using index
1PRIMARYdrefPRIMARY,IDX_5B45FC0CCD16E022IDX_5B45FC0CCD16E0225db.ta.id22100.00Using index
1PRIMARYerefPRIMARY,IDX_34DA0F079245DE54,IDX_34DA0F0796D072AA,...IDX_34DA0F0796D072AA5db.tw.id1100.00NULL
1PRIMARYfrefIDX_6AE5926BDF10B100IDX_6AE5926BDF10B1005db.e.id2100.00Using index
1PRIMARYgrefIDX_C87F908ADF10B100IDX_C87F908ADF10B1005db.e.id4100.00Using index
1PRIMARYhrefIDX_E4C5E45BDF10B100IDX_E4C5E45BDF10B1005db.e.id3100.00Using index
1PRIMARYirefIDX_33FF3C4DF10B100IDX_33FF3C4DF10B1005db.e.id2100.00Using index
1PRIMARYjrefIDX_3B909093DF10B100IDX_3B909093DF10B1005db.e.id1100.00Using index
1PRIMARYkrefIDX_9B50917CDF10B100IDX_9B50917CDF10B1005db.e.id5100.00Using index
1PRIMARYlrefIDX_10631447DF10B100IDX_10631447DF10B1005db.e.id6100.00Using index
1PRIMARYmrefIDX_31E107EFDF10B100IDX_31E107EFDF10B1005db.e.id2100.00Using index
1PRIMARYnrefIDX_CF3E3AECDF10B100IDX_CF3E3AECDF10B1005db.e.id1100.00Using index
1PRIMARYorefIDX_C13107E4DF10B100IDX_C13107E4DF10B1005db.e.id5100.00Using index
1PRIMARYp_erefPRIMARY,IDX_3027915D51906758IDX_3027915D519067584db.e.id1100.00Using index
1PRIMARYqrefIDX_7185325951906758IDX_71853259519067585db.e.id1100.00Using index
1PRIMARYref<auto_key0><auto_key0>4db.e.id10100.00NULL
4DERIVEDe_2indexPRIMARY,IDX_34DA0F079245DE54,IDX_34DA0F0796D072AA,...PRIMARY4NULL339578100.00Using index
4DERIVEDm_2refIDX_31E107EFDF10B100IDX_31E107EFDF10B1005db.e_2.id2100.00NULL
2DEPENDENT SUBQUERYe_innereq_refPRIMARYPRIMARY4func1100.00Using index
2DEPENDENT SUBQUERYf_2refIDX_6AE5926BDF10B100IDX_6AE5926BDF10B1005func2100.00Using where; Using index
3DEPENDENT SUBQUERYf_innereq_refPRIMARYPRIMARY4db.f_2.id1100.00NULL
3DEPENDENT SUBQUERYf_erefPRIMARY,IDX_21EE4B441666F235,IDX_21EE4B441F1F2A24IDX_21EE4B441666F2354db.f_2.id18100.00Using where; Using index
3DEPENDENT SUBQUERYeeq_refPRIMARYPRIMARY4db.f_e.r_id1100.00NULL

More thoughts

The present indices, as far as I understand, are for the foreign key relations between the tables. I believe that I require more indices than those, but I have no clue how/where to put them. I also read that left joins can be very costly, but if the relations might be null or non existant, they are required, from what I've read. Am I wrong with this assumption? Are there better ways to count these kinds of relations?

此外,这些性能问题,只发生在一些数据示例(一些a.id),而不是其他。我还没有发现,哪些场景的查询是快速的,哪些需要永远。

irlmq6kh

irlmq6kh1#

你可能会得到巨大的,不切实际的,计数值。
相反,应该这样做:

SELECT  ( SELECT COUNT(*) FROM a WHERE ... ) AS a_count,
        ( SELECT COUNT(*) FROM b WHERE ... ) AS b_count,
        ...
        ;

我没有完全理解目标是什么。我不知道WHERE子句中的内容。如果我没有给您指出正确的方向,请将代码简化为2-3个计数 * 并 * 提供示例输入 * 和 * 输出。
如果您需要计数在输出中按列显示,请参见标记[pivot-table]

相关问题