2012-09-25 3 views
3

일부 하위 쿼리 (내부 선택)가있는 쿼리가 있는데, 성능, 하나의 큰 쿼리 또는 작은 쿼리를 많이 더 잘 작동하도록 노력하고있어, 시도가 어렵고 시간 차이가 내 서버에서 항상 변하기 때문에 시간이 많이 걸립니다.MYSQL 쿼리 최적화, 다중 쿼리 또는 하나의 큰 쿼리

아래의 쿼리를 사용하여 내 웹 사이트에 페이지 매김 (오프셋 및 제한)을 사용하여 한 번에 10 개의 결과를 표시합니다.

SELECT adverts.*, breed.breed, breed.type, sellers.profile_name, sellers.logo, users.user_level , 
round(sqrt((((adverts.latitude - '51.558430') * (adverts.latitude - '51.558430')) * 69.1 * 69.1) + ((adverts.longitude - '-0.0069345') * (adverts.longitude - '-0.0069345') * 53 * 53)), 1) as distance, 
(SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename, 
(SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id) AS num_photos 
FROM adverts 
LEFT JOIN breed ON adverts.breed_id = breed.breed_id 
LEFT JOIN sellers ON (adverts.user_id = sellers.user_id) 
LEFT JOIN users ON (adverts.user_id = users.user_id) 
WHERE (adverts.status = 1) AND (adverts.approved = 1) 
AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719) AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613) 
having (distance <= '20') 
ORDER BY distance ASC 
LIMIT 0,10 

는 메인 쿼리에서 아래의 2 내부 선택을 제거하는 것이 더 있을까하고 내 PHP는 루프에서, 2 번 루프에서 각 레코드에 대해, 10 번을 선택 전화를?

(SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename, 
(SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id) AS num_photos 

답변

1

않도록 하위 쿼리

나는 당신의 내면의 선택을 알고있는 것처럼

, 그들은 두 가지 용도로 사용 관련 이미지 어떤 이름 및 관련 이미지의 수를 찾을 수 있습니다. 행 당신 '에 대한 쿼리의 일부를 수행

SELECT …, 
     advert_images.image_name AS imagename, 
     COUNT(advert_images.advert_id) AS num_photos, 
     … 
FROM … 
    LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id 
… 
GROUP BY adverts.advert_id 
… 
LIMIT 0,10 

나는이 시도하지 않은,하지만 아마도 MySQL의 엔진은 똑똑 : 당신은 아마 왼쪽을 사용하여 두 대신 내부 선택의 조인 달성 할 수 실제로 돌아온다.

에 대한 모든 보증이image name 주어진 쿼리에 대해이 쿼리가 반환됩니다. 재현 가능한 결과를 원한다면, 거기에 몇 가지 집계 함수를 사용해야합니다. MIN(advert_images.image_name)을 입력하여 사전 식으로 첫 번째 이미지를 선택하십시오.

선택 별도하지만 루프

만약 쿼리가 여전히 계산 결과의 모든 행이, 당신은 아마를 수행하여 오프 정말 더 낫다 advert_images 테이블을 검사합니다 즉 이상 작동하지 않습니다 두 번째 쿼리. 당신은 그러나 for 루프를 피하려고, 대신 단일 쿼리에서 모든 행을 가져올 수 있습니다이 쿼리에

SELECT advert_images.image_name AS imagename, 
     COUNT(advert_images.advert_id) AS num_photos 
FROM advert_images 
WHERE advert_images.advert_id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 
GROUP BY advert_images.advert_id 

10 개 개의 매개 변수는 현재 발생하고있는 결과의 열 행에 해당합니다. 사진이 인이없는 광고는 그 결과에 전혀 포함되지 않습니다. 따라서 코드에 num_photos을 0으로 설정하고 imagenameNULL으로 설정하십시오.

임시 테이블

당신이 메모리 테이블에 명시 적으로 일시적으로 사용하는 것입니다 수행하려고하는지 달성하는 또 다른 방법 : 먼저 당신이 관심있는 결과를 선택을 한 다음 모든 검색 관련 정보.

CREATE TEMPORARY TABLE tmp 
SELECT adverts.advert_id, round(…) as distance 
FROM adverts 
WHERE (adverts.status = 1) AND (adverts.approved = 1) 
    AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719) 
    AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613) 
HAVING (distance <= 20) 
ORDER BY distance ASC 
LIMIT 0,10; 

SELECT tmp.distance, adverts.*, … 
     advert_images.image_name AS imagename, 
     COUNT(advert_images.advert_id) AS num_photos, 
     … 
FROM tmp 
    INNER JOIN adverts ON tmp.advert_id = adverts.advert_id 
    LEFT JOIN breed ON adverts.breed_id = breed.breed_id 
    LEFT JOIN sellers ON adverts.user_id = sellers.user_id 
    LEFT JOIN users ON adverts.user_id = users.user_id 
    LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id 
GROUP BY adverts.advert_id 
ORDER BY tmp.distance ASC; 

DROP TABLE tmp; 

이렇게하면 현재 작업중인 결과에 대해서만 다른 모든 테이블을 쿼리 할 수 ​​있습니다.결국, advert_images 테이블에 대해서는 여러 가지 행이 필요할 수 있다는 점을 제외하고는 거의 마법이 없습니다.

전항의 접근에 요인을

건물에 가입 하위 쿼리로, 당신도 임시 테이블을 관리하지 않도록, 그 대신에 하위 쿼리를 사용할 수 있습니다

SELECT sub.distance, adverts.*, … 
     advert_images.image_name AS imagename, 
     COUNT(advert_images.advert_id) AS num_photos, 
     … 
FROM (SELECT adverts.advert_id, round(…) as distance 
     FROM adverts 
     WHERE (adverts.status = 1) AND (adverts.approved = 1) 
      AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719) 
      AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613) 
     HAVING (distance <= 20) 
     ORDER BY distance ASC 
     LIMIT 0,10; 
    ) AS sub 
    INNER JOIN adverts ON sub.advert_id = adverts.advert_id 
    LEFT JOIN breed ON adverts.breed_id = breed.breed_id 
    LEFT JOIN sellers ON (adverts.user_id = sellers.user_id) 
    LEFT JOIN users ON (adverts.user_id = users.user_id) 
    LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id 
GROUP BY adverts.advert_id 
ORDER BY sub.distance ASC 

다시 당신이 관련을 결정 행은 adverts 테이블의 데이터 만 사용하고 다른 테이블의 필수 행만 조인하십시오. 대부분 중간 결과는 내부적으로 임시 테이블에 저장되지만, SQL 서버가 결정해야합니다.

+0

안녕하세요. 자세한 답변을 보내 주셔서 감사합니다. 내가 그룹에 언급 한 첫 번째 방법으로 내부 선택을 제거하려고했지만 쿼리가 원래보다 느리게 진행되었습니다. 임시 테이블은 좋은 생각처럼 들리지만 웹 사이트가 매우 바쁠 때마다 쿼리가 서버에서 매초 약 10 회 실행될 때 제대로 작동합니까? – user1052096

+0

@ user1052096, 임시 테이블 접근 방식에 대한 두 개의 쿼리가 서로 충분히 근접한 한 영향은 거의 없습니다. 임시 테이블은 연결에 대해 로컬이므로 어떤 이름 충돌도 발생하지 않습니다. 그리고'tmp' 테이블의 메모리 소비량은 두 번째 쿼리 결과의 많은 열과 비교할 때 작아야하므로 결합 된 솔루션은 원래 쿼리보다 적은 메모리를 사용하게 될 것입니다. 그러나 나는 다른 생각을 가지고 있습니다. 나는 잠시 후에 나의 대답을 편집 할 것입니다. – MvG

+0

안녕하세요, MvG, 작동하지 않는 하위 쿼리를 사용하여 업데이트 주셔서 감사합니다. 하위 쿼리를 실행하는 경우 0.02 초로 실행되지만, advert_id를 선택하지 않고 하위 쿼리를 실행하면 0.01으로 두 배 빠르게 실행됩니다. – user1052096

0

나는 MySQL이 파일 정렬 + 임시 테이블을 사용하여 쿼리를 수행한다고 생각합니다. 그래서 큰 테이블에서 제안하면 더 나은 결과를 얻을 수 있습니다. 일반적으로 작은 쿼리를 수행 한 다음 1을 크게 수행하는 것이 좋습니다.

+0

안녕하세요. 계산 된 필드 인 거리별로 정렬하기 때문에 테이블이 클 때 속도가 느려지는 파일롯을 사용합니다. 기본 쿼리에서 테이블의 모든 레코드에 대해 2 개의 내부 선택이 실행됩니까? 그렇다면 결과 세트에서 2 개의 내부 선택을 실행하는 것이 더 빠를 것이라고 생각했을 것입니다. – user1052096

+0

예 내부 선택이 모든 행에 수행됩니다. –