2017-05-08 9 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 | 
+----+------+-----------+---------------+ 

これは、個々の列に名前を付けることができなくなりますが、それは問合せを処理できるすべてのものです。または、単一のサブクエリで処理できます。

typescase ...

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 convertingを使用できます。 SUBSTRING(DATE,1,10)は、より速くより安全なdate(date)に置き換えることができます。

また、カラムdateの名前を付けることは悪い考えです。これはMySQLの関数名であり、問​​題を引き起こす可能性があります。

関連する問題