2010-01-13 3 views
2

이 쿼리를 완료 할 수는 있지만 25 초가 걸립니다. 너무 깁니다! 이 쿼리를 어떻게 최적화 할 수 있습니까? 변수에 문 밖에서 : (startDate를, INTERVAL 1 MONTH) 이 SQL select 쿼리를 최적화하는 방법은 무엇입니까?

SELECT COUNT(DISTINCT u1.User_ID) AS total 
FROM UserClicks u1 
INNER JOIN (SELECT DISTINCT User_ID 
       FROM UserClicks 
      WHERE (Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate)) u2 
      ON u1.User_ID = u2.User_ID 
WHERE (u1.Date BETWEEN :startDate AND :endDate) 

+0

UserClicks.User_ID 필드가 고유하지 않고 색인이 생성되어 있습니까? 그러면 쿼리의 두 DISTINCT 부분을 제거 할 수 있습니다. 어쨌든, 나는 @Parrots가 당신의 대답을 아래에 가지고 있다고 생각합니다. – JMD

+0

@andrew : 시작하기 전에 한 달 전과 시작일과 종료일 사이를 클릭 한 사람들이 정말로하고 싶은 일이 있습니까? (아래의 Quassnoi 주석을 참조하십시오.) – Hogan

답변

2
SELECT COUNT(*) AS total 
FROM (
     SELECT DISTINCT User_ID 
     FROM UserClicks 
     WHERE Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate 
     ) u1 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM UserClicks u2 
     WHERE u2.User_ID = u1.User_ID 
       AND u2.Date BETWEEN :startDate AND :endDate 
     ) 

(User_ID, Date)에 복합 인덱스를 만듭니다 :

CREATE INDEX ix_userclicks_user_date ON UserClicks (User_ID, Date) 

당신이 가지고있는 경우도 날짜 열에서 인덱스를 추가하는 경우

SELECT COUNT(DISTINCT UserClicks.User_ID) AS total 
FROM UserClicks 
WHERE (UserClicks.Date BETWEEN :startDate AND :endDate) 
AND (UserClicks.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate) 

도움이 될 사용자 수가 적지 만 클릭 수가 많고 표가 Users 인 경우 Users을 사용할 수 있습니다. 테이블 대신 DISTINCT는 :

SELECT COUNT(*) 
FROM Users u 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM UserClicks uc1 
     WHERE uc1.UserId = u.Id 
       AND uc1.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate 
     ) 
     AND EXISTS 
     (
     SELECT NULL 
     FROM UserClicks uc2 
     WHERE uc2.UserId = u.Id 
       AND u2.Date BETWEEN :startDate AND :endDate 
     ) 
+0

복합 색인을 만든 후에는 무엇을 변경해야합니까? – Andrew

+0

... 복합 색인이 고유해야합니까? (어리석은 질문이라면 죄송합니다.) – Andrew

+0

복합 색인은 검색어가 더 빨리 (특히 두 번째 검색어가) 실행되도록 도움을줍니다. – Quassnoi

0

당신이 DATE_SUB를 이동하는 시도 해 봤나 MySQL 데이터베이스에 사용되는? UserClicks.Date의 색인이 있습니까?

0

중첩 된 쌍의 선택 항목을 실행하는 대신 하나의 select 문을 사용하지 않는 이유는 무엇입니까? 지금은 기본적으로 두 가지 쿼리를 실행하고 있습니다. 이 시도 :

ALTER TABLE `UserClicks` ADD INDEX ( `Date`); 
+0

원래 쿼리가 반환하지 않는 값을 반환합니다. – Quassnoi

+0

색인을 추가하면 무엇을 의미합니까? 그걸 보여줄 수 있니? – Andrew

+0

@Quassnoi 쿼리가 어떻게 달라 지나요? 결과가 어떻게됩니까? 나는 그 차이를보기가 힘듭니다. 중첩 된 것들은 기본적으로 "시작일과 종료일 사이의 모든 사람들을 얻으십시오"라고 말하면서 시작일과 +1 달 사이에 모든 사람들을 얻습니다. 그냥 AND 연산과 다른 점은 무엇입니까? – Parrots

0

MySQL은 서브 쿼리를 처리 할 때 인덱스를 무시하는 경향이있다, 그래서 모든 행을 처리 할 수있다. 대신 자체 조인은 어떻습니까? 이것은 내 머리 꼭대기에서 약간 벗어나므로 정확하지 않을 수도 있지만 최소한 올바른 방향으로 향해야합니다.

SELECT COUNT(DISTINCT u1.User_ID) AS total 
FROM UserClicks AS u1 
JOIN UserClicks AS u2 USING (User_ID) 
WHERE u1.Date BETWEEN :startDate AND :endDate 
AND u2.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate) 
관련 문제