2017-05-08 56 views
0

我工作的MySQL数据库。我需要将多个(超过10个)表格合并成一个表格。为了做到这一点,我遵循典型的加入方式。如何加快多个连接的SQL查询的执行?

Select * from 
table_1 
Join table_2 
on(table_1.id = table_2.id) 
Join table_3 
on(table_1.id = table_3.id) 

它的工作原理,但我在执行期间遭受了很多。有没有其他好方法来优化我的代码?以下是我的代码示例:

SELECT 
distinct 
u.Id, 
oc.dt, 
Daily_Number_Outgoing_Calls,  
Daily_Number_Incoming_Calls,  
Daily_duration_Outgoing_Calls 

FROM 
creditfix.users u 

JOIN 

#1 Daily_No_Out_Calls 
    (
     SELECT 
     cl.uId,SUBSTRING(DATE,1,10) as dt, 
     count(1) as Daily_Number_Outgoing_Calls 

     From creditfix.call_logs as cl 
      WHERE 
       cl.`type`=2 #out going calls only 
     GROUP by cl.uId,dt 
    ) oc 
    ON (u.Id=oc.Uid) 

#2 Daily_No_In_Calls 
    JOIN 
    (
     SELECT 
     cl.uId, SUBSTRING(DATE,1,10) as dt, 
     count(1) as Daily_Number_Incoming_Calls 
     From creditfix.call_logs as cl 
     WHERE 
      cl.`type`=1 #incoming calls only 
     GROUP by cl.uId,dt 
    ) ic 
    ON (u.Id=ic.Uid) 

#3 Daily_duration_Out_Calls 
    JOIN 
    (
     SELECT 
     cl.uId,SUBSTRING(DATE,1,10) as dt, 
     (sum(duration)) as Daily_duration_Outgoing_Calls 
     From creditfix.call_logs as cl 
     WHERE 
      cl.`type`=2 #out going calls only 
     GROUP by cl.uId,dt 
    ) od 
    ON (u.Id=od.uid) 
    # It goes on like this... 
+2

请不要使用列号代替'GROUP BY'中的名称,这会使查询更难以阅读。 – Barmar

+4

当你编辑代码时,如果你在'SELECT'列表中插入列,就会变得脆弱。 – Barmar

+2

性能问题是因为您不只是加入表格,而是加入子查询。这些没有像真正的表那样的索引,所以它们很难让MySQL优化。 – Barmar

回答

5

看起来你并不需要使用独立的子查询的每一列,你应该能够做他们在一个子查询。

我也不认为你应该需要DISTINCT主查询。

SELECT 
    u.Id, 
    cl.dt, 
    cl.Daily_Number_Outgoing_Calls,  
    cl.Daily_Number_Incoming_Calls,  
    cl.Daily_duration_Outgoing_Calls, 
    cl.Daily_duration_Incoming_Calls #.... for keep on adding like this 

FROM creditfix.users u 
JOIN (
    SELECT uId, SUBSTRING(DATE, 1, 10) AS dt, 
     SUM(`type`=2) AS Daily_Number_Outgoing_Calls, 
     SUM(`type`=1) AS Daily_Number_Incoming_Calls, 
     SUM(IF(`type`=2, duration, 0)) AS Daily_duration_Outgoing_Calls, 
     SUM(IF(`type`=1, duration, 0)) AS Daily_duration_Incoming_Calls 
    FROM creditfix.call_logs as cl 
    GROUP BY uId, dt) AS cl 
ON u.Id = cl.uId 

在子查询用来获取所有计数逻辑见multiple query same table but in different columns mysql。在评论中提到

+0

绝对是一个好主意,但出于好奇,为什么要使用子查询呢?没有一个人似乎更容易理解。 –

+0

1个可能的原因是,如果creditfix.users与call_logs有多对多关系。通过在加入之前进行汇总,可以消除人为增加计数的风险。 – xQbert

+2

@xQbert这是一些查询中的问题,但在这里我们似乎要加入一个唯一的ID,并且我们将通过该外键对子查询进行分组。更一般的原因是聚合后的连接减少了连接的大小。 – Barmar

3

由于,这些都不是简单的连接,这些都是子查询联接,这使得优化更加困难。您必须优化每个子查询,或者设法不需要子查询。

既然你想获得通话记录每个用户和类型的信息在指定的日子,这可以用一个简单的完成通过加入和组。没有必要的子查询。

select 
    ??? 
from 
    creditfix.users u 
join 
    creditfix.call_logs as cl on u.id = cl.uid 
where 
    substring(date,1,10)=??? 
group by 
    cl.uid, cl.type; 

所以复制它似乎你要去的,呼叫的数量和它们的总时间...

select 
    u.id, cl.type, count(cl.id) as num_calls, sum(cl.duration) as duration 
from 
    creditfix.users u 
join 
    creditfix.call_logs as cl on u.id = cl.uid 
where 
    substring(date,1,10)='2017-03-18' 
group by 
    cl.uid, cl.type; 

你会得到这样的事情。

+----+------+-----------+---------------+ 
| id | type | num_calls | call_duration | 
+----+------+-----------+---------------+ 
| 1 | 1 |   3 |   20 | 
| 1 | 3 |   1 |   10 | 
| 1 | 5 |   2 |    4 | 
| 2 | 5 |   1 |    4 | 
+----+------+-----------+---------------+ 

这失去了命名每个单独列的能力,但这是接收查询可以处理的任何东西。或者它可以用一个子查询处理。

types可以用case被命名...

case cl.type 
    when 1 then 'outgoing' 
    when 2 then 'incoming' 
    when 3 then ... 
    else cl.type 
end as type 

...但是这需要艰苦的查询编码幻数。你最好制作一个表来存储关于类型的信息并加入。


子查询本身在这里有一个潜在的性能问题:substring(date,1,10) = '2017-03-08'。如果date没有被索引,查询将不得不做全表扫描。

date作为一个字符串引入了性能问题。数据库必须对每一行执行字符串操作,尽管可能MySQL足够聪明地使用索引。而datetime type是一个简单的数字比较,将使用索引。它也有点小,8个字节。

它允许您使用the date and time functions without convertingSUBSTRING(DATE,1,10)可以用更快更安全的date(date)来代替。

同时命名列date是一个坏主意,它是MySQL中的一个函数名称,可能会导致问题。