[sql] 조인 대 하위 쿼리

나는 구식 MySQL 사용자이며 항상 JOIN하위 쿼리 보다 선호 했습니다. 그러나 요즘에는 모든 사람들이 하위 쿼리를 사용하고 있습니다. 이유를 모르겠습니다.

차이가 있는지 스스로 판단 할 이론적 지식이 부족합니다. 하위 쿼리는 a만큼 우수 JOIN하므로 걱정할 것이 없습니까?



답변

MySQL 매뉴얼 ( 13.2.10.11 서브 쿼리를 조인으로 재 작성 )에서 발췌 :

LEFT [OUTER] JOIN은 서버가 서버를 더 잘 최적화 할 수 있기 때문에 동등한 하위 쿼리보다 빠를 수 있습니다. 사실 MySQL 서버에만 국한된 것은 아닙니다.

따라서 하위 쿼리는보다 느릴 수 LEFT [OUTER] JOIN있지만 내 의견으로는 가독성이 약간 높습니다.


답변

하위 쿼리는 “A에서 팩트 가져 오기, B에서 팩트에 대한 조건부”형식의 문제를 해결하기위한 논리적으로 올바른 방법입니다. 이러한 경우 조인을 수행하는 것보다 하위 쿼리에 B를 사용하는 것이 더 논리적입니다. B와의 여러 경기로 인해 A에서 중복 사실을 얻는 것에 대해주의 할 필요가 없으므로 실제적인 의미에서 더 안전합니다.

그러나 실제로 대답은 대개 성능에 달려 있습니다. 일부 옵티마이 저는 조인과 하위 쿼리가 주어지면 레몬을 빨아 들이고, 다른 옵티마이 저는 다른 방법으로 레몬을 빨아 들이며, 이는 옵티 마이저, DBMS 버전 및 쿼리에 따라 다릅니다.

역사적으로 명시 적 조인은 일반적으로 승리하므로 조인이 더 좋아진다는 기존의 지혜가 있지만 옵티마이 저는 항상 더 좋아지고 있습니다. 따라서 논리적으로 일관된 방식으로 쿼리를 먼저 작성하고 성능 제약 조건이이를 보증하는 경우 재구성하는 것을 선호합니다.


답변

대부분의 경우 JOINs는 하위 쿼리보다 빠르며 하위 쿼리가 더 빠른 경우는 거의 없습니다.

에서 JOIN의 RDBMS는 쿼리에 대해 더 나은 실행 계획을 생성 할 수 있으며 모든 쿼리를 실행하고 처리 할 모든 데이터를로드 하위 쿼리는 달리, 데이터를 처리 할 수로드되어야 하는지를 예측하고 시간을 절약 할 수 있습니다 .

하위 쿼리에서 좋은 점은 JOINs 보다 읽기 쉽다는 것입니다 . 이것이 대부분의 새로운 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.
가능하면 이것을 피하십시오. 거대한 데이터 세트에서 쿼리 속도가 크게 느려질 수 있습니다. 그러나 하위 쿼리에 대한 참조가 없으면 fooDB 서버에서 정적 컨텐츠로 최적화 할 수 있으며 한 번만 평가할 수 있습니다.

Where-statement의 하위 쿼리

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

운이 좋으면 DB는이를 내부적으로로 최적화합니다 JOIN. 그렇지 않은 경우 쿼리는 fooselect-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 문에없는 경우 성능이 크게 향상 될 수 있습니다.


답변

우선, 두 가지를 먼저 비교하려면 쿼리를 하위 쿼리와 구별하여 다음을 수행해야합니다.

  1. 항상 조인으로 작성된 해당하는 동등한 쿼리가있는 서브 쿼리 클래스
  2. 조인을 사용하여 다시 작성할 수없는 하위 쿼리 클래스

첫 번째 쿼리 클래스 의 경우 우수한 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 |
+-------------------+

두 번째 시도는 평균값을 계산하기 위해 중복을 계산한다는 점에서 유스 케이스에서 잘못된 결과를 산출한다는 것이 분명합니다. 또한 distinctjoin-based 문 을 사용한 경우 에도 세 번의 점수 발생 중 하나를 잘못 유지한다는 점에서 문제가 해결 되지않습니다3.9 . 올바른 케이스에 대한 계정입니다 TWO (2) 의 발생 3.9우리가 실제로 가지고 주어진 점수 TWO (2) 우리의 쿼리 기준을 준수하는지 그 점수로 학생을.

경우에 따라 성능 문제 외에도 하위 쿼리가 가장 안전한 방법 인 것 같습니다.