[sql] 조인 대 하위 쿼리
나는 구식 MySQL 사용자이며 항상 JOIN
하위 쿼리 보다 선호 했습니다. 그러나 요즘에는 모든 사람들이 하위 쿼리를 사용하고 있습니다. 이유를 모르겠습니다.
차이가 있는지 스스로 판단 할 이론적 지식이 부족합니다. 하위 쿼리는 a만큼 우수 JOIN
하므로 걱정할 것이 없습니까?
답변
MySQL 매뉴얼 ( 13.2.10.11 서브 쿼리를 조인으로 재 작성 )에서 발췌 :
LEFT [OUTER] JOIN은 서버가 서버를 더 잘 최적화 할 수 있기 때문에 동등한 하위 쿼리보다 빠를 수 있습니다. 사실 MySQL 서버에만 국한된 것은 아닙니다.
따라서 하위 쿼리는보다 느릴 수 LEFT [OUTER] JOIN
있지만 내 의견으로는 가독성이 약간 높습니다.
답변
하위 쿼리는 “A에서 팩트 가져 오기, B에서 팩트에 대한 조건부”형식의 문제를 해결하기위한 논리적으로 올바른 방법입니다. 이러한 경우 조인을 수행하는 것보다 하위 쿼리에 B를 사용하는 것이 더 논리적입니다. B와의 여러 경기로 인해 A에서 중복 사실을 얻는 것에 대해주의 할 필요가 없으므로 실제적인 의미에서 더 안전합니다.
그러나 실제로 대답은 대개 성능에 달려 있습니다. 일부 옵티마이 저는 조인과 하위 쿼리가 주어지면 레몬을 빨아 들이고, 다른 옵티마이 저는 다른 방법으로 레몬을 빨아 들이며, 이는 옵티 마이저, DBMS 버전 및 쿼리에 따라 다릅니다.
역사적으로 명시 적 조인은 일반적으로 승리하므로 조인이 더 좋아진다는 기존의 지혜가 있지만 옵티마이 저는 항상 더 좋아지고 있습니다. 따라서 논리적으로 일관된 방식으로 쿼리를 먼저 작성하고 성능 제약 조건이이를 보증하는 경우 재구성하는 것을 선호합니다.
답변
대부분의 경우 JOIN
s는 하위 쿼리보다 빠르며 하위 쿼리가 더 빠른 경우는 거의 없습니다.
에서 JOIN
의 RDBMS는 쿼리에 대해 더 나은 실행 계획을 생성 할 수 있으며 모든 쿼리를 실행하고 처리 할 모든 데이터를로드 하위 쿼리는 달리, 데이터를 처리 할 수로드되어야 하는지를 예측하고 시간을 절약 할 수 있습니다 .
하위 쿼리에서 좋은 점은 JOIN
s 보다 읽기 쉽다는 것입니다 . 이것이 대부분의 새로운 SQL 사람들이 선호하는 이유입니다. 쉬운 방법입니다. 그러나 성능면에서 JOINS는 읽기가 쉽지 않지만 대부분의 경우 더 좋습니다.
답변
EXPLAIN을 사용하여 데이터베이스가 데이터에서 쿼리를 실행하는 방법을 확인하십시오. 이 답변에는 거대한 “의존”이 있습니다 …
PostgreSQL은 하위 쿼리가 하위 쿼리보다 빠르다고 생각 될 때 하위 쿼리를 조인에 조인하거나 하위 쿼리에 조인 할 수 있습니다. 그것은 모두 데이터, 인덱스, 상관 관계, 데이터 양, 쿼리 등에 따라 다릅니다.
답변
2010 년에 나는이 질문의 저자에 합류했고에 대해 강력하게 투표했을 JOIN
것이지만 훨씬 더 많은 경험 (특히 MySQL에서)으로 다음과 같이 말할 수 있습니다. 여기에 여러 답변을 읽었습니다. 일부 언급 된 하위 쿼리는 더 빠르지 만 설명이 부족합니다. 나는이 (매우) 늦게 답변을 제공 할 수 있기를 바랍니다.
우선, 가장 중요한 말을하겠습니다 : 하위 쿼리에는 여러 가지 형태가 있습니다
그리고 두 번째 중요한 진술 : 크기 문제
하위 쿼리를 사용 하는 경우 DB 서버가 하위 쿼리를 실행하는 방법을 알고 있어야 합니다. 특히 하위 쿼리가 한 번 또는 모든 행에 대해 평가되는 경우!
반면에 최신 DB-Server는 많은 것을 최적화 할 수 있습니다. 하위 쿼리는 쿼리 최적화에 도움이되지만 최신 버전의 DB-Server는 최적화를 더 이상 사용하지 않을 수 있습니다.
선택 필드의 하위 쿼리
SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo
의 모든 결과 행에 대해 하위 쿼리가 실행됩니다 foo
.
가능하면 이것을 피하십시오. 거대한 데이터 세트에서 쿼리 속도가 크게 느려질 수 있습니다. 그러나 하위 쿼리에 대한 참조가 없으면 foo
DB 서버에서 정적 컨텐츠로 최적화 할 수 있으며 한 번만 평가할 수 있습니다.
Where-statement의 하위 쿼리
SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)
운이 좋으면 DB는이를 내부적으로로 최적화합니다 JOIN
. 그렇지 않은 경우 쿼리는 foo
select-type과 같은 결과뿐만 아니라의 모든 행에 대해 하위 쿼리를 실행하기 때문에 대규모 데이터 세트에서 쿼리 속도가 매우 느려집니다 .
Join 문에서 하위 쿼리
SELECT moo, bar
FROM foo
LEFT JOIN (
SELECT MIN(bar), me FROM wilco GROUP BY me
) ON moo = me
이것은 흥미 롭다. 우리는 JOIN
하위 쿼리와 결합 합니다. 그리고 여기서 우리는 하위 쿼리의 진정한 강점을 얻습니다. 행의 수백만 데이터 집합 상상 wilco
만 몇 별개을 me
. 거대한 테이블에 대해 조인하는 대신 이제 더 작은 임시 테이블에 조인 할 수 있습니다. 데이터베이스 크기에 따라 쿼리 속도가 훨씬 빨라질 수 있습니다. CREATE TEMPORARY TABLE ...
and와 같은 효과를 낼 수 INSERT INTO ... SELECT ...
있으므로 매우 복잡한 쿼리에서 가독성이 향상되지만 반복 가능한 읽기 격리 수준에서 데이터 집합을 잠글 수 있습니다.
중첩 된 하위 쿼리
SELECT moo, bar
FROM (
SELECT moo, CONCAT(roger, wilco) AS bar
FROM foo
GROUP BY moo
HAVING bar LIKE 'SpaceQ%'
) AS temp_foo
ORDER BY bar
하위 쿼리를 여러 수준으로 중첩 할 수 있습니다. 결과를 그룹화하거나 정렬해야하는 경우 거대한 데이터 세트에 도움이 될 수 있습니다. 일반적으로 DB-Server는이를 위해 임시 테이블을 생성하지만 때로는 전체 테이블에서 정렬 할 필요가없고 결과 집합에서만 정렬 할 필요가 있습니다. 이는 테이블 크기에 따라 훨씬 더 나은 성능을 제공 할 수 있습니다.
결론
하위 쿼리는 a를 대체 JOIN
하지 않으므로 이와 같이 사용해서는 안됩니다 (가능한 경우에도). 겸손한 의견으로는 하위 쿼리를 올바르게 사용하는 것이 빠른 대체로 사용됩니다 CREATE TEMPORARY TABLE ...
. 좋은 하위 쿼리는의 ON
문 에서 수행 할 수없는 방식으로 데이터 집합을 줄 JOIN
입니다. 하위 쿼리에 키워드 중 하나가 GROUP BY
있거나 DISTINCT
선택 필드 나 where 문에없는 경우 성능이 크게 향상 될 수 있습니다.
답변
우선, 두 가지를 먼저 비교하려면 쿼리를 하위 쿼리와 구별하여 다음을 수행해야합니다.
- 항상 조인으로 작성된 해당하는 동등한 쿼리가있는 서브 쿼리 클래스
- 조인을 사용하여 다시 작성할 수없는 하위 쿼리 클래스
첫 번째 쿼리 클래스 의 경우 우수한 RDBMS는 조인 및 하위 쿼리를 동등한 것으로 간주하고 동일한 쿼리 계획을 생성합니다.
요즘 mysql도 그렇게합니다.
그럼에도 불구하고 때로는 그렇지 않지만 이것이 조인이 항상 이길 것이라는 것을 의미하지는 않습니다-mysql에서 하위 쿼리를 사용할 때 성능이 향상되었습니다. (예를 들어 mysql 플래너가 비용을 정확하게 추정하지 못하게하는 것이 있고 플래너가 join-variant 및 subquery-variant를 동일하게 보지 못하면 하위 쿼리는 특정 경로를 강제하여 조인보다 성능이 뛰어납니다).
결론은 어느 것이 더 잘 수행되는지 확인하려면 조인 및 하위 쿼리 변형에 대해 쿼리를 테스트해야한다는 것입니다.
두 번째 클래스 의 경우 조인을 사용하여 해당 쿼리를 다시 작성할 수 없으므로 하위 쿼리는 필요한 작업을 수행하는 자연적인 방법이므로 차별하지 않아야합니다.
답변
인용 된 답변에서 강조되지 않은 것은 특정 (사용) 사례에서 발생할 수있는 중복 및 문제가있는 결과입니다.
(Marcelo Cantos가 언급했지만)
Stanford의 Lagunita 코스에서 SQL에 대한 예제를 인용하겠습니다.
학생 테이블
+------+--------+------+--------+
| sID | sName | GPA | sizeHS |
+------+--------+------+--------+
| 123 | Amy | 3.9 | 1000 |
| 234 | Bob | 3.6 | 1500 |
| 345 | Craig | 3.5 | 500 |
| 456 | Doris | 3.9 | 1000 |
| 567 | Edward | 2.9 | 2000 |
| 678 | Fay | 3.8 | 200 |
| 789 | Gary | 3.4 | 800 |
| 987 | Helen | 3.7 | 800 |
| 876 | Irene | 3.9 | 400 |
| 765 | Jay | 2.9 | 1500 |
| 654 | Amy | 3.9 | 1000 |
| 543 | Craig | 3.4 | 2000 |
+------+--------+------+--------+
테이블 적용
(특정 대학 및 전공에 적용)
+------+----------+----------------+----------+
| sID | cName | major | decision |
+------+----------+----------------+----------+
| 123 | Stanford | CS | Y |
| 123 | Stanford | EE | N |
| 123 | Berkeley | CS | Y |
| 123 | Cornell | EE | Y |
| 234 | Berkeley | biology | N |
| 345 | MIT | bioengineering | Y |
| 345 | Cornell | bioengineering | N |
| 345 | Cornell | CS | Y |
| 345 | Cornell | EE | N |
| 678 | Stanford | history | Y |
| 987 | Stanford | CS | Y |
| 987 | Berkeley | CS | Y |
| 876 | Stanford | CS | N |
| 876 | MIT | biology | Y |
| 876 | MIT | marine biology | N |
| 765 | Stanford | history | Y |
| 765 | Cornell | history | N |
| 765 | Cornell | psychology | Y |
| 543 | MIT | CS | N |
+------+----------+----------------+----------+
CS
대학에 관계없이 전공 에 지원 한 학생들의 GPA 점수를 찾아 봅시다.
하위 쿼리 사용 :
select GPA from Student where sID in (select sID from Apply where major = 'CS');
+------+
| GPA |
+------+
| 3.9 |
| 3.5 |
| 3.7 |
| 3.9 |
| 3.4 |
+------+
이 결과 집합의 평균값은 다음과 같습니다.
select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');
+--------------------+
| avg(GPA) |
+--------------------+
| 3.6800000000000006 |
+--------------------+
조인 사용 :
select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';
+------+
| GPA |
+------+
| 3.9 |
| 3.9 |
| 3.5 |
| 3.7 |
| 3.7 |
| 3.9 |
| 3.4 |
+------+
이 결과 집합의 평균 값 :
select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';
+-------------------+
| avg(GPA) |
+-------------------+
| 3.714285714285714 |
+-------------------+
두 번째 시도는 평균값을 계산하기 위해 중복을 계산한다는 점에서 유스 케이스에서 잘못된 결과를 산출한다는 것이 분명합니다. 또한 distinct
join-based 문 을 사용한 경우 에도 세 번의 점수 발생 중 하나를 잘못 유지한다는 점에서 문제가 해결 되지 는 않습니다3.9
. 올바른 케이스에 대한 계정입니다 TWO (2) 의 발생 3.9
우리가 실제로 가지고 주어진 점수 TWO (2) 우리의 쿼리 기준을 준수하는지 그 점수로 학생을.
경우에 따라 성능 문제 외에도 하위 쿼리가 가장 안전한 방법 인 것 같습니다.