나는 구식 MySQL 사용자이며 항상 JOIN
하위 쿼리 보다 선호 했습니다. 그러나 요즘에는 모든 사람들이 하위 쿼리를 사용하고 있습니다. 이유를 모르겠습니다.
차이가 있는지 스스로 판단 할 이론적 지식이 부족합니다. 하위 쿼리는 a만큼 우수 JOIN
하므로 걱정할 것이 없습니까?
나는 구식 MySQL 사용자이며 항상 JOIN
하위 쿼리 보다 선호 했습니다. 그러나 요즘에는 모든 사람들이 하위 쿼리를 사용하고 있습니다. 이유를 모르겠습니다.
차이가 있는지 스스로 판단 할 이론적 지식이 부족합니다. 하위 쿼리는 a만큼 우수 JOIN
하므로 걱정할 것이 없습니까?
답변:
MySQL 매뉴얼 ( 13.2.10.11 서브 쿼리를 조인으로 재 작성 )에서 발췌 :
LEFT [OUTER] JOIN은 서버가 서버를 더 잘 최적화 할 수 있기 때문에 동등한 하위 쿼리보다 빠를 수 있습니다. 사실 MySQL 서버에만 국한된 것은 아닙니다.
따라서 하위 쿼리는보다 느릴 수 LEFT [OUTER] JOIN
있지만 내 의견으로는 가독성이 약간 높습니다.
Join
과 sub query
구문이 다르므로 비교할 수없는 가독성은 SQL 구문에 능숙하다면 가독성이 높습니다. 성능이 더 중요합니다.
하위 쿼리는 "A에서 팩트 가져 오기, B에서 팩트에 대한 조건부"형식의 문제를 해결하기위한 논리적으로 올바른 방법입니다. 이러한 경우 조인을 수행하는 것보다 하위 쿼리에 B를 사용하는 것이 더 논리적입니다. B와의 여러 경기로 인해 A에서 중복 사실을 얻는 것에 대해주의 할 필요가 없으므로 실제적인 의미에서 더 안전합니다.
그러나 실제로 대답은 대개 성능에 달려 있습니다. 일부 옵티마이 저는 조인과 하위 쿼리가 주어지면 레몬을 빨아 들이고, 다른 옵티마이 저는 다른 방법으로 레몬을 빨아 들이며, 이는 옵티 마이저, DBMS 버전 및 쿼리에 따라 다릅니다.
역사적으로 명시 적 조인은 일반적으로 승리하므로 조인이 더 좋아진다는 기존의 지혜가 있지만 옵티마이 저는 항상 더 좋아지고 있습니다. 따라서 논리적으로 일관된 방식으로 쿼리를 먼저 작성하고 성능 제약 조건이이를 보증하는 경우 재구성하는 것을 선호합니다.
select custid from cust join bought using (custid) where price > 500
. 고객이 값 비싼 품목을 여러 개 구입 한 경우 이중 상품이 제공됩니다. 이 문제를 해결하려면 select custid from cust where exists (select * from bought where custid = cust.custid and price > 500)
. select distinct …
대신 사용할 수 있지만 최적화 프로그램이나 평가자에게 더 많은 작업이 필요한 경우가 많습니다.
대부분의 경우 JOIN
s는 하위 쿼리보다 빠르며 하위 쿼리가 더 빠른 경우는 거의 없습니다.
에서 JOIN
의 RDBMS는 쿼리에 대해 더 나은 실행 계획을 생성 할 수 있으며 모든 쿼리를 실행하고 처리 할 모든 데이터를로드 하위 쿼리는 달리, 데이터를 처리 할 수로드되어야 하는지를 예측하고 시간을 절약 할 수 있습니다 .
하위 쿼리에서 좋은 점은 JOIN
s 보다 읽기 쉽다는 것입니다 . 이것이 대부분의 새로운 SQL 사람들이 선호하는 이유입니다. 쉬운 방법입니다. 그러나 성능면에서 JOINS는 읽기가 쉽지 않지만 대부분의 경우 더 좋습니다.
select * from a where a.x = (select b.x form b where b.id = a.id)
조인에 비해 매우 작습니다. 이것은 매우 특정한 문제이지만 경우에 따라 몇 시간에서 몇 분으로 안내합니다.
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 서버에서 정적 컨텐츠로 최적화 할 수 있으며 한 번만 평가할 수 있습니다.
SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)
운이 좋으면 DB는이를 내부적으로로 최적화합니다 JOIN
. 그렇지 않은 경우 쿼리는 foo
select-type과 같은 결과뿐만 아니라의 모든 행에 대해 하위 쿼리를 실행하기 때문에 대규모 데이터 세트에서 쿼리 속도가 매우 느려집니다 .
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 문에없는 경우 성능이 크게 향상 될 수 있습니다.
Sub-queries in the Join-statement
(1) 생성은 매우 긴 시간이 걸릴 수 부 조회 테이블 자체로부터 산출했다. (2) 결과 파생 테이블이 인덱싱되지 않습니다. 이 두 가지만으로도 SQL 속도가 크게 느려질 수 있습니다.
10
레코드로 줄일 수 있더라도 인덱스가 없기 때문에 다른 테이블에 참여할 때 임시 테이블이없는 것보다 9 배 더 많은 데이터 레코드를 쿼리 할 가능성이 있습니다. BTW 내 db (MySQL) 에서이 문제가 발생했습니다. 제 경우에는 하위 쿼리를 사용하는 SELECT list
것이 훨씬 빠릅니다.
EXPLAIN
최적화하기 전에 쿼리에 사용해야합니다 . 이전 set profiling=1
테이블을 사용하면 임시 테이블에 병목 현상이 있는지 쉽게 알 수 있습니다. 그리고 인덱스조차도 처리 시간이 필요하지만 B- 트리는 레코드 쿼리를 최적화하지만 10 레코드 테이블은 수백만 레코드의 인덱스보다 훨씬 빠를 수 있습니다. 그러나 필드 크기 및 유형과 같은 여러 요인에 따라 다릅니다.
우선, 두 가지를 먼저 비교하려면 쿼리를 하위 쿼리와 구별하여 다음을 수행해야합니다.
첫 번째 쿼리 클래스 의 경우 우수한 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) 우리의 쿼리 기준을 준수하는지 그 점수로 학생을.
경우에 따라 성능 문제 외에도 하위 쿼리가 가장 안전한 방법 인 것 같습니다.
하위 쿼리를 포함하는 많은 Transact-SQL 문을 조인으로 구성 할 수 있습니다. 다른 질문은 하위 쿼리로만 제기 할 수 있습니다. Transact-SQL에서는 일반적으로 하위 쿼리를 포함하는 문과 의미가 동등한 버전의 성능 차이가 없습니다. 그러나 존재 여부를 확인해야하는 경우 조인의 성능이 향상됩니다. 그렇지 않으면 외부 쿼리의 각 결과에 대해 중첩 쿼리를 처리하여 중복을 제거해야합니다. 이러한 경우 조인 방식으로 더 나은 결과를 얻을 수 있습니다.
그래서 당신이 같은 것이 필요하다면
select * from t1 where exists select * from t2 where t2.parent=t1.id
대신 join을 사용하십시오. 다른 경우에는 아무런 차이가 없습니다.
나는 말합니다 : 하위 쿼리에 대한 함수 를 생성 하면 클러스터의 문제가 제거되고 하위 쿼리에 대한 추가 논리를 구현할 수 있습니다. 따라서 가능하면 하위 쿼리를위한 함수를 만드는 것이 좋습니다.
코드의 혼란은 큰 문제이며 업계는 수십 년 동안이를 피하기 위해 노력해 왔습니다.
NOT EXISTS
. 성능 , 실패 안전 (널 (null) 열의 경우) 및 가독성과 같은 여러 가지 이유로 A가 NOT EXISTS
이깁니다 LEFT OUTER JOIN
. sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join
오래된 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은 정확히 같은 수의 행을 검사하지만 1 초는 3 초가 걸리고 1은 거의 순간에 가까운 것으로 나타났습니다. 이야기의 교훈? 성능이 중요하지 않은 경우 (필요하지 않은 경우) 여러 가지 방법으로 시도하여 어느 것이 가장 빠른지 확인하십시오.
과...
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보다 알아내는 데 훨씬 오래 걸립니다.
id
하고 이름은 지정해서는 안된다고 말하고 catid
있습니까? 내 DB 액세스를 최적화하려고하면 학습이 도움이 될 수 있습니다.
두 경우와 같이 내 관찰에 따르면 테이블에 100,000 개 미만의 레코드가 있으면 조인이 빠르게 작동합니다.
그러나 테이블에 100,000 개가 넘는 레코드가있는 경우 하위 쿼리가 가장 좋습니다.
아래 쿼리에서 생성 한 500,000 개의 레코드가있는 하나의 테이블이 있으며 결과 시간은
SELECT *
FROM crv.workorder_details wd
inner join crv.workorder wr on wr.workorder_id = wd.workorder_id;
결과 : 13.3 초
select *
from crv.workorder_details
where workorder_id in (select workorder_id from crv.workorder)
결과 : 1.65 초
서브 쿼리는 일반적으로 단일 행을 원자 값으로 리턴하는 데 사용되지만 IN 키워드를 사용하여 여러 행과 값을 비교하는 데 사용될 수 있습니다. 대상 목록, WHERE 절 등을 포함하여 SQL 문의 거의 모든 의미있는 지점에서 허용됩니다. 간단한 하위 쿼리를 검색 조건으로 사용할 수 있습니다. 예를 들어, 한 쌍의 테이블 사이 :
SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');
하위 쿼리 결과에 일반 값 연산자를 사용하려면 하나의 필드 만 반환해야합니다. 다른 값 집합 내에 단일 값이 있는지 확인하려면 IN을 사용하십시오.
SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');
이것은 조인 조건이 테이블 B 등에서 일치하는 레코드를 찾지 못하는 경우에도 테이블 A 및 B에서 물건을 조인하려는 LEFT-JOIN과는 분명히 다릅니다.
속도가 걱정된다면 데이터베이스를 확인하고 좋은 쿼리를 작성하고 성능에 큰 차이가 있는지 확인해야합니다.
MySQL 버전 : 5.5.28-0ubuntu0.12.04.2-log
또한 JOIN이 항상 MySQL의 하위 쿼리보다 낫다는 인상을 받았지만 EXPLAIN은 판단을 내리는 더 좋은 방법입니다. 다음은 JOIN보다 하위 쿼리가 더 잘 작동하는 예입니다.
다음은 3 개의 하위 쿼리가있는 쿼리입니다.
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;
설명 표시 :
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| 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 |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
JOIN과 동일한 쿼리는 다음과 같습니다.
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;
출력은 다음과 같습니다.
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| 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 |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
rows
열을 비교 하면 차이점을 알 수 있으며 JOIN을 사용한 쿼리는을 사용 Using temporary; Using filesort
합니다.
물론 두 쿼리를 모두 실행하면 첫 번째 쿼리는 0.02 초 후에 수행되고 두 번째 쿼리는 1 분 후에도 완료되지 않으므로 EXPLAIN은 이러한 쿼리를 올바르게 설명했습니다.
list_tag
테이블 에 INNER JOIN이없는 경우, 즉 제거하는 경우
AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL
첫 번째 쿼리에서
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403
두 번째 쿼리에서 EXPLAIN은 두 쿼리에 대해 동일한 수의 행을 반환하고 두 쿼리 모두 똑같이 빠르게 실행됩니다.
서브 쿼리는 즉시 집계 함수를 계산할 수 있습니다. 예 : 책의 최소 가격을 찾고이 가격으로 판매 된 모든 책을 얻으십시오. 1) 서브 쿼리 사용 :
SELECT titles, price
FROM Books, Orders
WHERE price =
(SELECT MIN(price)
FROM Orders) AND (Books.ID=Orders.ID);
2) JOIN 사용
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;
GROUP BY
다른 테이블이있는 여러 개 : stackoverflow.com/questions/11415284/… 하위 쿼리는 엄격하게 더 일반적 인 것으로 보입니다. MySQL man : dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html |을 참조하십시오. dev.mysql.com/doc/refman/5.7/en/rewriting-subqueries.html
어떤 사람들은 "일부 RDBMS는 다시 쓸 수 있다고 하위 쿼리를 A는에 가입 하거나이 조인 A와 하위 쿼리 . 그것은 한 빨리 다른 것보다 생각하는 경우"하지만,이 문으로 복잡한 쿼리하지 확실히, 간단한 경우에 적용 하위 쿼리 하는 실제 원인 성능 문제.
차이점은 두 번째 조인 테이블에 기본 테이블보다 훨씬 많은 데이터가있는 경우에만 나타납니다. 나는 아래와 같은 경험을했다 ...
우리는 10 만 항목의 사용자 테이블과 약 3 십만 항목의 회원 데이터 (우정)를 가졌습니다. 친구와 그들의 데이터를 가져 오기 위해 조인 진술 이었지만 크게 지연되었습니다. 그러나 멤버십 테이블에 적은 양의 데이터 만 있으면 잘 작동했습니다. 하위 쿼리를 사용하도록 변경하면 정상적으로 작동합니다.
그러나 그 동안 조인 쿼리는 기본 테이블보다 적은 수의 항목을 가진 다른 테이블과 작업하고 있습니다.
따라서 조인 및 하위 쿼리 문이 제대로 작동하고 데이터와 상황에 따라 다릅니다.
나는 단지 같은 문제에 대해 생각하고 있지만 FROM 부분에서 하위 쿼리를 사용하고 있습니다. 큰 테이블의 연결 및 쿼리가 필요합니다. "슬레이브"테이블에는 2 천 8 백만 개의 레코드가 있지만 결과는 128 개의 작은 결과 빅 데이터입니다! MAX () 함수를 사용하고 있습니다.
먼저 올바른 방법이라고 생각하기 때문에 LEFT JOIN을 사용하고 있습니다. mysql은 최적화 할 수 있습니다. 테스트를 위해 두 번째로 JOIN에 대해 하위 선택을 다시 작성합니다.
왼쪽 조인 런타임 : 1.12 초 하위 선택 런타임 : 0.06 초
조인보다 하위 선택이 18 배 빠릅니다! 초 키토 전진에서. subselect는 끔찍하지만 결과는 ...
join을 사용하여 쿼리 속도를 높이려면
"내부 조인 / 가입"의 경우 조건을 대신 "ON"상태로 사용하지 마십시오. 예 :
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'
"왼쪽 / 오른쪽 조인"의 경우 "ON"조건에서는 사용하지 마십시오. 왼쪽 / 오른쪽 조인을 사용하면 하나의 테이블에 대한 모든 행을 가져 오므로 "온"에서 사용하지 않습니다. "Where"조건을 사용하십시오