简体   繁体   English

如何通过多个联接加快执行SQL查询的速度?

[英]How to speed up the execution of sql query with multiple joins?

I am working on MySql database. 我正在使用MySql数据库。 Where I need to merge information form multiple(more than 10) tables into a single one. 我需要将多个(超过10个)表中的信息合并到一个表中。 In order to do that I am following a typical joining style. 为此,我遵循一种典型的加入方式。

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

It works but I suffer a lot during execution time. 它可以工作,但是在执行期间我受了很多苦。 Is there any other good way to optimize my code? 还有其他优化我的代码的好方法吗? Following is the sample of my code: 以下是我的代码示例:

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...

It looks like you don't need to use separate subqueries for each column, you should be able to do them in a single subquery. 看起来您不需要为每个列使用单独的子查询,您应该能够在单个子查询中进行操作。

I also don't think you should need DISTINCT in the main query. 我也认为您在主查询中不需要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

See multiple query same table but in different columns mysql for the logic used in the subquery to get all the counts. 请参阅多个查询相同的表,但在mysql的不同列中,以获取子查询中用于获取所有计数的逻辑。

As mentioned in the comments, these aren't simple joins, these are subquery joins which makes optimization more difficult. 如评论中所述,这些不是简单的联接,它们是子查询联接,这使得优化更加困难。 You'll have to optimize each subquery, or figure a way to not need subqueries. 您必须优化每个子查询,或者找到一种不需要子查询的方法。

Since you want to get call log info per user and type for a given day, this can be done with a simple join and group by. 由于您要获取给定日期的每个用户的呼叫日志信息并进行键入,因此可以通过简单的加入和分组方式来完成。 No subqueries necessary. 无需子查询。

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;

So to replicate what it appears you're going for, the number of calls and their total duration... 因此,要复制您要查找的内容,通话次数及其总持续时间...

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;

You'll get something like this. 你会得到这样的东西。

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

This loses the ability to name each individual column, but that's something whatever is receiving the query can handle. 这样就失去了为每个单独的列命名的能力,但这就是接收查询可以处理的任何事情。 Or it can be handled with a single subquery. 或者可以使用单个子查询来处理。

The types can be named with a case ... types可以用case来命名...

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

...but this requires hard coding magic numbers in queries. ...但是这需要对查询中的幻数进行硬编码。 you'd be better off making a table to store info about the types and joining with that. 您最好制作一个表来存储有关类型的信息并加入其中。


The subqueries themselves have a potential performance problem here: substring(date,1,10) = '2017-03-08' . 子查询本身在此处存在潜在的性能问题: substring(date,1,10) = '2017-03-08' If date is not indexed, the queries will have to do a full table scan. 如果未索引date ,则查询将必须进行全表扫描。

Having date as a string introduces performance problems. 使用date作为字符串会带来性能问题。 The database has to perform string operations on each row, though it's possible MySQL is smart enough to use the index. 数据库必须在每一行上执行字符串操作,尽管MySQL可能足够聪明以使用索引。 Whereas with a datetime type is a simple numeric comparison and will use an index. 而使用datetime类型则是一个简单的数字比较,并且将使用索引。 It's also a bit smaller, 8 bytes. 它也更小,8字节。

It allows you to use the date and time functions without converting . 它使您无需转换即可使用日期和时间函数 SUBSTRING(DATE,1,10) can be replaced with the faster and safer date(date) . SUBSTRING(DATE,1,10)可以替换为更快更安全的date(date)

Also naming a column date is a bad idea, it's a function name in MySQL and might cause problems. 命名列date也是一个坏主意,它是MySQL中的函数名称,可能会引起问题。

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

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