development

조인 대 하위 쿼리

big-blog 2020. 9. 29. 08:06
반응형

조인 대 하위 쿼리


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

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


MySQL 매뉴얼 ( 13.2.10.11 Rewriting Subqueries as Joins )에서 발췌 :

LEFT [OUTER] JOIN은 서버가이를 더 잘 최적화 할 수 있기 때문에 동등한 하위 쿼리보다 빠를 수 있습니다. 이는 MySQL Server에만 국한되지 않는 사실입니다.

따라서 하위 쿼리는보다 느릴 수 LEFT [OUTER] JOIN있지만 제 생각에는 그 강도가 가독성이 약간 더 높습니다.


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

그러나 실질적으로 대답은 일반적으로 성능에 달려 있습니다. 일부 옵티마이 저는 조인 대 하위 쿼리가 주어지면 레몬을 빨아 들이고, 일부는 다른 방식으로 레몬을 빨아들입니다. 이것은 옵티 마이저 별, DBMS 버전 별 및 쿼리 별입니다.

역사적으로 명시 적 조인이 일반적으로이기므로 조인이 더 나아지지만 옵티마이 저는 항상 더 좋아지고 있으므로 먼저 논리적으로 일관된 방식으로 쿼리를 작성한 다음 성능 제약이이를 보장하는 경우 재구성하는 것을 선호합니다.


대부분의 경우 JOINs는 하위 쿼리보다 빠르며 하위 쿼리가 더 빠른 경우는 매우 드뭅니다.

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

하위 쿼리의 좋은 점은 JOINs 보다 가독성이 높다는 것입니다. 이것이 대부분의 새로운 SQL 사용자가 선호하는 이유입니다. 쉬운 방법입니다. 그러나 성능면에서 JOINS는 읽기가 어렵지 않지만 대부분의 경우 더 좋습니다.


EXPLAIN을 사용하여 데이터베이스가 데이터에 대해 쿼리를 실행하는 방법을 확인합니다. 이 답변에는 엄청난 "의존적"이 있습니다.

PostgreSQL은 하나가 다른 것보다 빠르다고 생각할 때 하위 쿼리를 조인에 재 작성하거나 하위 쿼리에 대한 조인을 다시 작성할 수 있습니다. 그것은 모두 데이터, 인덱스, 상관 관계, 데이터 양, 쿼리 등에 따라 다릅니다.


2010 년에 나는이 질문의 저자와 함께했을 것이고 JOIN. 그러나 훨씬 더 많은 경험을 통해 (특히 MySQL에서) 다음과 같이 말할 수 있습니다. 예 하위 쿼리가 더 좋을 수 있습니다. 여기에서 여러 답변을 읽었습니다. 일부는 하위 쿼리가 더 빠르다고 말했지만 좋은 설명이 부족했습니다. 이 (매우) 늦은 답변을 제공 할 수 있기를 바랍니다.

우선, 가장 중요한 부분을 말씀 드리겠습니다. 다양한 형태의 하위 쿼리가 있습니다.

두 번째 중요한 진술 : 크기가 중요합니다.

하위 쿼리를 사용 하는 경우 DB-Server가 하위 쿼리를 실행하는 방법을 알고 있어야 합니다. 특히 하위 쿼리가 한 번 또는 모든 행에 대해 평가되는 경우! 반면에 최신 DB-Server는 많은 것을 최적화 할 수 있습니다. 경우에 따라 하위 쿼리가 쿼리 최적화에 도움이되지만 최신 버전의 DB-Server에서는 최적화가 더 이상 사용되지 않을 수 있습니다.

선택 필드의 하위 쿼리

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

의 모든 결과 행에 대해 하위 쿼리가 실행됩니다 foo. 가능하면이를 피하십시오. 거대한 데이터 세트에 대한 쿼리 속도가 크게 느려질 수 있습니다. 그러나 하위 쿼리에에 대한 참조가 없으면 fooDB 서버에서 정적 콘텐츠로 최적화 할 수 있으며 한 번만 평가할 수 있습니다.

Where 문의 하위 쿼리

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 ...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를 동일하게 보지 않으면 하위 쿼리가 특정 경로를 강제하여 조인보다 성능이 우수 할 수 있습니다).

결론은 어느 것이 더 나은 성능을 발휘하는지 확인하려면 조인 및 하위 쿼리 변형 모두에 대해 쿼리를 테스트해야한다는 것입니다.

두 번째 클래스 의 경우 조인을 사용하여 쿼리를 다시 작성할 수 없으며 이러한 경우 하위 쿼리가 필요한 작업을 수행하는 자연스러운 방법이며이를 구별해서는 안되므로 비교가 의미가 없습니다.


SQL Server에 대한 MSDN 설명서에 따르면

하위 쿼리를 포함하는 많은 Transact-SQL 문을 조인으로 공식화 할 수 있습니다. 다른 질문은 하위 쿼리로만 제기 할 수 있습니다. Transact-SQL에서는 일반적으로 하위 쿼리를 포함하는 문과 그렇지 않은 의미 상 동등한 버전간에 성능 차이가 없습니다. 그러나 존재를 확인해야하는 경우에는 조인이 더 나은 성능을 제공합니다. 그렇지 않으면 중복을 제거하기 위해 외부 쿼리의 각 결과에 대해 중첩 된 쿼리를 처리해야합니다. 이러한 경우 조인 방식이 더 나은 결과를 제공합니다.

그래서 당신이 뭔가 필요하다면

select * from t1 where exists select * from t2 where t2.parent=t1.id

대신 조인을 사용하십시오. 다른 경우에는 차이가 없습니다.

나는 말한다 : 하위 쿼리에 대한 함수생성 하면 혼란의 문제가 제거되고 하위 쿼리에 추가 논리를 구현할 수 있습니다. 따라서 가능한 한 하위 쿼리에 대한 함수를 만드는 것이 좋습니다.

코드의 혼란은 큰 문제이며 업계는이를 방지하기 위해 수십 년 동안 노력해 왔습니다.


인용 된 답변에서 과소 강조된 것은 특정 (사용) 사례에서 발생할 수있는 중복 및 문제가있는 결과 문제라고 생각합니다 .

(Marcelo Cantos가 언급하지만)

SQL에 관한 Stanford의 Lagunita 과정의 예를 인용하겠습니다.

학생 테이블

+------+--------+------+--------+
| 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조인 기반 문과 함께 를 사용 하면 세 번의 점수 발생 중 하나를 잘못 유지하므로 문제가 제거 되지않습니다3.9 . 올바른 경우는 쿼리 기준을 준수하는 해당 점수를 가진 학생 이 실제로 TWO (2) 명인 경우 점수가 TWO (2) 발생하는 경우를 설명 3.9하는 것 입니다.

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


이전 Mambo CMS의 매우 큰 데이터베이스에서 실행합니다.

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 초

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

~ 3 초

EXPLAIN은 정확히 동일한 수의 행을 검사하지만 하나는 3 초가 걸리고 하나는 거의 즉각적인 행임을 보여줍니다. 이야기의 교훈? 성능이 중요한 경우 (언제 그렇지 않습니까?) 여러 가지 방법을 시도하고 어떤 것이 가장 빠른지 확인하십시오.

과...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 초

다시, 동일한 결과, 동일한 수의 행이 검사되었습니다. 내 생각에 DISTINCT mos_content.catid는 DISTINCT mos_categories.id보다 알아내는 데 훨씬 더 오래 걸립니다.


Subqueries are generally used to return a single row as an atomic value, though they may be used to compare values against multiple rows with the IN keyword. They are allowed at nearly any meaningful point in a SQL statement, including the target list, the WHERE clause, and so on. A simple sub-query could be used as a search condition. For example, between a pair of tables:

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

Note that using a normal value operator on the results of a sub-query requires that only one field must be returned. If you're interested in checking for the existence of a single value within a set of other values, use IN:

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

This is obviously different from say a LEFT-JOIN where you just want to join stuff from table A and B even if the join-condition doesn't find any matching record in table B, etc.

If you're just worried about speed you'll have to check with your database and write a good query and see if there's any significant difference in performance.


As per my observation like two cases, if a table has less then 100,000 records then the join will work fast.

But in the case that a table has more than 100,000 records then a subquery is best result.

I have one table that has 500,000 records on that I created below query and its result time is like

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

Result : 13.3 Seconds

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

Result : 1.65 Seconds


MySQL version: 5.5.28-0ubuntu0.12.04.2-log

I was also under the impression that JOIN is always better than a sub-query in MySQL, but EXPLAIN is a better way to make a judgment. Here is an example where sub queries work better than JOINs.

Here is my query with 3 sub-queries:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

EXPLAIN shows:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

The same query with JOINs is:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

and the output is:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

A comparison of the rows column tells the difference and the query with JOINs is using Using temporary; Using filesort.

Of course when I run both the queries, the first one is done in 0.02 secs, the second one does not complete even after 1 min, so EXPLAIN explained these queries properly.

If I do not have the INNER JOIN on the list_tag table i.e. if I remove

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

from the first query and correspondingly:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

from the second query, then EXPLAIN returns the same number of rows for both queries and both these queries run equally fast.


Subqueries have ability to calculate aggregation functions on a fly. E.g. Find minimal price of the book and get all books which are sold with this price. 1) Using Subqueries:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) using JOINs

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;

  • A general rule is that joins are faster in most cases (99%).
  • The more data tables have, the subqueries are slower.
  • The less data tables have, the subqueries have equivalent speed as joins.
  • The subqueries are simpler, easier to understand, and easier to read.
  • Most of the web and app frameworks and their "ORM"s and "Active record"s generate queries with subqueries, because with subqueries are easier to split responsibility, maintain code, etc.
  • For smaller web sites or apps subqueries are OK, but for larger web sites and apps you will often have to rewrite generated queries to join queries, especial if a query uses many subqueries in the query.

Some people say "some RDBMS can rewrite a subquery to a join or a join to a subquery when it thinks one is faster than the other.", but this statement applies to simple cases, surely not for complicated queries with subqueries which actually cause a problems in performance.


The difference is only seen when the second joining table has significantly more data than the primary table. I had an experience like below...

We had a users table of one hundred thousand entries and their membership data (friendship) about 3 hundred thousand entries. It was a join statement in order to take friends and their data, but with a great delay. But it was working fine where there was only a small amount of data in the membership table. Once we changed it to use a sub-query it worked fine.

But in the mean time the join queries are working with other tables that have fewer entries than the primary table.

So I think the join and sub query statements are working fine and it depends on the data and the situation.


These days, many dbs can optimize subqueries and joins. Thus, you just gotto examine your query using explain and see which one is faster. If there is not much difference in performance, I prefer to use subquery as they are simple and easier to understand.


I just thinking about the same problem, but I am using subquery in the FROM part. I need connect and query from large tables, the "slave" table have 28 million record but the result is only 128 so small result big data! I am using MAX() function on it.

First I am using LEFT JOIN because I think that is the correct way, the mysql can optimalize etc. Second time just for testing, I rewrite to sub-select against the JOIN.

LEFT JOIN runtime: 1.12s SUB-SELECT runtime: 0.06s

18 times faster the subselect than the join! Just in the chokito adv. The subselect looks terrible but the result ...


If you want to speed up your query using join:

For "inner join/join", Don't use where condition instead use it in "ON" condition. Eg:

     select id,name from table1 a  
   join table2 b on a.name=b.name
   where id='123'

 Try,

    select id,name from table1 a  
   join table2 b on a.name=b.name and a.id='123'

For "Left/Right Join", Don't use in "ON" condition, Because if you use left/right join it will get all rows for any one table.So, No use of using it in "On". So, Try to use "Where" condition

참고URL : https://stackoverflow.com/questions/2577174/join-vs-sub-query

반응형