如何通过多个连接加快 sql 查询的执行速度?
How to speed up the execution of sql query with multiple joins?
我正在处理 MySql 数据库。我需要将多个(超过 10 个)表的信息合并到一个表中。为此,我遵循典型的加入方式。
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...
看起来您不需要为每一列使用单独的子查询,您应该能够在单个子查询中完成它们。
我也不认为你应该在主查询中需要 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。
如评论中所述,这些不是简单的连接,而是子查询连接,这使得优化更加困难。您必须优化每个子查询,或者想出一种不需要子查询的方法。
由于您想获取特定日期每个用户和类型的通话记录信息,这可以通过简单的加入和分组依据来完成。无需子查询。
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
...但这需要在查询中对幻数进行硬编码。你最好制作一个 table 来存储有关类型的信息并与之连接。
子查询本身在此处存在潜在的性能问题:substring(date,1,10) = '2017-03-08'
。如果 date
未编入索引,查询将必须进行完整的 table 扫描。
将 date
作为字符串会引入性能问题。数据库必须对每一行执行字符串操作,尽管 MySQL 可能足够聪明以使用索引。而 datetime
type 是一个简单的数字比较,将使用索引。它也有点小,8 个字节。
它允许您使用 the date and time functions without converting。 SUBSTRING(DATE,1,10)
可以换成更快更安全的date(date)
.
同时将列命名为 date
是个坏主意,它是 MySQL 中的函数名称,可能会导致问题。
我正在处理 MySql 数据库。我需要将多个(超过 10 个)表的信息合并到一个表中。为此,我遵循典型的加入方式。
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...
看起来您不需要为每一列使用单独的子查询,您应该能够在单个子查询中完成它们。
我也不认为你应该在主查询中需要 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。
如评论中所述,这些不是简单的连接,而是子查询连接,这使得优化更加困难。您必须优化每个子查询,或者想出一种不需要子查询的方法。
由于您想获取特定日期每个用户和类型的通话记录信息,这可以通过简单的加入和分组依据来完成。无需子查询。
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
...但这需要在查询中对幻数进行硬编码。你最好制作一个 table 来存储有关类型的信息并与之连接。
子查询本身在此处存在潜在的性能问题:substring(date,1,10) = '2017-03-08'
。如果 date
未编入索引,查询将必须进行完整的 table 扫描。
将 date
作为字符串会引入性能问题。数据库必须对每一行执行字符串操作,尽管 MySQL 可能足够聪明以使用索引。而 datetime
type 是一个简单的数字比较,将使用索引。它也有点小,8 个字节。
它允许您使用 the date and time functions without converting。 SUBSTRING(DATE,1,10)
可以换成更快更安全的date(date)
.
同时将列命名为 date
是个坏主意,它是 MySQL 中的函数名称,可能会导致问题。