2017-05-08 15 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

가능한 원인은 creditfix.users에 call_logs가있는 many-to-many가있는 경우입니다. 참여하기 전에 집계하여 인위적으로 카운트를 증가시킬 위험을 제거하십시오. – 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 converting을 사용할 수 있습니다. SUBSTRING(DATE,1,10)은 더 빠르고 더 안전한 date(date)으로 대체 될 수 있습니다.

또한 열 date의 이름을 지정하는 것은 좋지 않은 생각이며, MySQL의 함수 이름이므로 문제가 발생할 수 있습니다.