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
id | select_type | table | type | possbile_keys | key | key_len | eq_ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | a | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using index; Using temporary; Using filesort |
1 | PRIMARY | b | ref | PRIMARY,IDX_548D5BBD166D1F9C | IDX_548D5BBD166D1F9C | 5 | const | 10 | 100.00 | Using index |
1 | PRIMARY | c | ref | PRIMARY,IDX_D09734DB23BCD4D0 | IDX_D09734DB23BCD4D0 | 5 | db.t.id | 7 | 100.00 | Using index |
1 | PRIMARY | d | ref | PRIMARY,IDX_5B45FC0CCD16E022 | IDX_5B45FC0CCD16E022 | 5 | db.ta.id | 22 | 100.00 | Using index |
1 | PRIMARY | e | ref | PRIMARY,IDX_34DA0F079245DE54,IDX_34DA0F0796D072AA,... | IDX_34DA0F0796D072AA | 5 | db.tw.id | 1 | 100.00 | NULL |
1 | PRIMARY | f | ref | IDX_6AE5926BDF10B100 | IDX_6AE5926BDF10B100 | 5 | db.e.id | 2 | 100.00 | Using index |
1 | PRIMARY | g | ref | IDX_C87F908ADF10B100 | IDX_C87F908ADF10B100 | 5 | db.e.id | 4 | 100.00 | Using index |
1 | PRIMARY | h | ref | IDX_E4C5E45BDF10B100 | IDX_E4C5E45BDF10B100 | 5 | db.e.id | 3 | 100.00 | Using index |
1 | PRIMARY | i | ref | IDX_33FF3C4DF10B100 | IDX_33FF3C4DF10B100 | 5 | db.e.id | 2 | 100.00 | Using index |
1 | PRIMARY | j | ref | IDX_3B909093DF10B100 | IDX_3B909093DF10B100 | 5 | db.e.id | 1 | 100.00 | Using index |
1 | PRIMARY | k | ref | IDX_9B50917CDF10B100 | IDX_9B50917CDF10B100 | 5 | db.e.id | 5 | 100.00 | Using index |
1 | PRIMARY | l | ref | IDX_10631447DF10B100 | IDX_10631447DF10B100 | 5 | db.e.id | 6 | 100.00 | Using index |
1 | PRIMARY | m | ref | IDX_31E107EFDF10B100 | IDX_31E107EFDF10B100 | 5 | db.e.id | 2 | 100.00 | Using index |
1 | PRIMARY | n | ref | IDX_CF3E3AECDF10B100 | IDX_CF3E3AECDF10B100 | 5 | db.e.id | 1 | 100.00 | Using index |
1 | PRIMARY | o | ref | IDX_C13107E4DF10B100 | IDX_C13107E4DF10B100 | 5 | db.e.id | 5 | 100.00 | Using index |
1 | PRIMARY | p_e | ref | PRIMARY,IDX_3027915D51906758 | IDX_3027915D51906758 | 4 | db.e.id | 1 | 100.00 | Using index |
1 | PRIMARY | q | ref | IDX_7185325951906758 | IDX_7185325951906758 | 5 | db.e.id | 1 | 100.00 | Using index |
1 | PRIMARY | ref | <auto_key0> | <auto_key0> | 4 | db.e.id | 10 | 100.00 | NULL | |
4 | DERIVED | e_2 | index | PRIMARY,IDX_34DA0F079245DE54,IDX_34DA0F0796D072AA,... | PRIMARY | 4 | NULL | 339578 | 100.00 | Using index |
4 | DERIVED | m_2 | ref | IDX_31E107EFDF10B100 | IDX_31E107EFDF10B100 | 5 | db.e_2.id | 2 | 100.00 | NULL |
2 | DEPENDENT SUBQUERY | e_inner | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 100.00 | Using index |
2 | DEPENDENT SUBQUERY | f_2 | ref | IDX_6AE5926BDF10B100 | IDX_6AE5926BDF10B100 | 5 | func | 2 | 100.00 | Using where; Using index |
3 | DEPENDENT SUBQUERY | f_inner | eq_ref | PRIMARY | PRIMARY | 4 | db.f_2.id | 1 | 100.00 | NULL |
3 | DEPENDENT SUBQUERY | f_e | ref | PRIMARY,IDX_21EE4B441666F235,IDX_21EE4B441F1F2A24 | IDX_21EE4B441666F235 | 4 | db.f_2.id | 18 | 100.00 | Using where; Using index |
3 | DEPENDENT SUBQUERY | e | eq_ref | PRIMARY | PRIMARY | 4 | db.f_e.r_id | 1 | 100.00 | NULL |
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),而不是其他。我还没有发现,哪些场景的查询是快速的,哪些需要永远。
1条答案
按热度按时间irlmq6kh1#
你可能会得到巨大的,不切实际的,计数值。
相反,应该这样做:
我没有完全理解目标是什么。我不知道
WHERE
子句中的内容。如果我没有给您指出正确的方向,请将代码简化为2-3个计数 * 并 * 提供示例输入 * 和 * 输出。如果您需要计数在输出中按列显示,请参见标记
[pivot-table]
。