어떤 SQL 쿼리가 더 빠릅니까? 조인 기준 또는 Where 절을 기준으로 필터링 하시겠습니까?


98

이 두 쿼리를 비교하십시오. 조인 기준이나 WHERE절 에 필터를 적용하는 것이 더 빠릅니까? 가능한 한 빨리 결과 세트를 줄이기 때문에 조인 기준에서 더 빠르다고 항상 느꼈지만 확실하지 않습니다.

나는 몇 가지 테스트를 만들어 볼 것이지만 또한 어떤 것이 더 명확하게 읽을 수 있는지에 대한 의견을 얻고 싶었습니다.

쿼리 1

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
INNER JOIN  TableB b
        ON  x.TableBID = b.ID
WHERE       a.ID = 1            /* <-- Filter here? */

쿼리 2

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
        AND a.ID = 1            /* <-- Or filter here? */
INNER JOIN  TableB b
        ON  x.TableBID = b.ID

편집하다

몇 가지 테스트를 실행 한 결과 결과는 실제로 매우 가깝지만 WHERE절은 실제로 약간 더 빠릅니다! =)

WHERE절에 필터를 적용하는 것이 더 합리적이라는 데 절대적으로 동의합니다 . 성능에 미치는 영향에 대해 궁금했습니다.

ELAPSED TIME WHERE CRITERIA : 143016 ms
ELAPSED TIME JOIN CRITERIA : 143256 ms

테스트

SET NOCOUNT ON;

DECLARE @num    INT,
        @iter   INT

SELECT  @num    = 1000, -- Number of records in TableA and TableB, the cross table is populated with a CROSS JOIN from A to B
        @iter   = 1000  -- Number of select iterations to perform

DECLARE @a TABLE (
        id INT
)

DECLARE @b TABLE (
        id INT
)

DECLARE @x TABLE (
        aid INT,
        bid INT
)

DECLARE @num_curr INT
SELECT  @num_curr = 1
        
WHILE (@num_curr <= @num)
BEGIN
    INSERT @a (id) SELECT @num_curr
    INSERT @b (id) SELECT @num_curr
    
    SELECT @num_curr = @num_curr + 1
END

INSERT      @x (aid, bid)
SELECT      a.id,
            b.id
FROM        @a a
CROSS JOIN  @b b

/*
    TEST
*/
DECLARE @begin_where    DATETIME,
        @end_where      DATETIME,
        @count_where    INT,
        @begin_join     DATETIME,
        @end_join       DATETIME,
        @count_join     INT,
        @curr           INT,
        @aid            INT

DECLARE @temp TABLE (
        curr    INT,
        aid     INT,
        bid     INT
)

DELETE FROM @temp

SELECT  @curr   = 0,
        @aid    = 50

SELECT  @begin_where = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
    INNER JOIN  @b b
            ON  x.bid = b.id
    WHERE       a.id = @aid
        
    SELECT @curr = @curr + 1
END
SELECT  @end_where = CURRENT_TIMESTAMP

SELECT  @count_where = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @curr = 0
SELECT  @begin_join = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
            AND a.id = @aid
    INNER JOIN  @b b
            ON  x.bid = b.id
    
    SELECT @curr = @curr + 1
END
SELECT  @end_join = CURRENT_TIMESTAMP

SELECT  @count_join = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @count_where AS count_where,
        @count_join AS count_join,
        DATEDIFF(millisecond, @begin_where, @end_where) AS elapsed_where,
        DATEDIFF(millisecond, @begin_join, @end_join) AS elapsed_join

10
데이터에 따라 WHERE 대 JOIN 기준은 다른 결과 집합을 반환 할 수 있습니다.
OMG Ponies

4
@OMG Ponies는 매우 사실이지만 많은 경우에는 그렇지 않습니다.
Jon Erickson

2
나는 차이가 5 % 이하라고 부르지 않을 것입니다. 2 %% 차이에 대한 유의성을 원하면 테스트를 1000 번 더 잘 실행하여 무작위가 아닌지 확인하십시오.
TomTom 2011

장점은 x.ID은 다음 가능성이 a.ID보다 개선을 볼 것 인 경우 때문에 가입하기 전에 데이터를 필터링에
MikeT

답변:


65

성능면에서 동일 함 (동일한 계획 생성)

논리적으로, 당신은 당신이 대체 할 경우 여전히 감각이 작동해야 INNER JOIN로모그래퍼을 LEFT JOIN.

귀하의 경우 이것은 다음과 같습니다.

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
        AND a.ID = 1
LEFT JOIN
        TableB b
ON      x.TableBID = b.ID

아니면 이거:

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
LEFT JOIN
        TableB b
ON      b.id = x.TableBID
WHERE   a.id = 1

전자 쿼리는 a.id이외의 실제 일치 항목을 반환하지 1않으므로 후자의 구문 (with WHERE)은 논리적으로 더 일관성이 있습니다.


세트를 그릴 때 두 번째 경우가 더 일관된 이유를 이해했습니다. 이전 쿼리에서 제약 조건 a.id = 1은 교차점을 제외한 왼쪽 부분이 아닌 교차점에만 적용됩니다.
FtheBuilder

1
첫 번째 예에는 행이있을 수 있고 a.id != 1다른 행에는 행만있을 수 있습니다 a.id = 1.
FtheBuilder

1
귀하의 언어가 명확하지 않습니다. "논리적으로 ..."와 "논리적으로 더 일관된"작동이 의미가없는 경우에도 여전히 의미가있는 작업을 만들어야합니다. 다시 말씀해 주시겠습니까?
philipxy

24

내부 조인의 경우 기준을 어디에 두든 상관 없습니다. SQL 컴파일러는 둘 다 조인 아래에서 필터링이 발생하는 실행 계획으로 변환합니다 (즉, 필터 표현식이 조인 조건에있는 것처럼).

필터의 위치가 쿼리의 의미를 변경하기 때문에 외부 조인은 다른 문제입니다.


따라서 내부 조인에서 먼저 필터를 계산 한 다음 필터의 출력을 다른 테이블과 조인하거나 먼저 두 테이블을 조인 한 다음 필터를 적용합니까?
애쉬 윈

@Remus Rusanu-Outer-join의 경우 의미 체계가 어떻게 변경되는지 자세히 설명해 주시겠습니까? 필터 위치에 따라 다른 결과를 얻었지만 이유를 이해할 수 없습니다
Ananth

3
외부 조인이있는 @Ananth는 JOIN 조건이 일치하지 않는 조인 된 테이블의 모든 열에 대해 NULL을 얻습니다. 필터는 NULL을 충족하지 않고 행을 제거하여 OUTER 조인을 INNER 조인으로 전환합니다.
Remus Rusanu

@Ananth 귀하의 의견에 따라 필요한 최적화를 달성했습니다. 내 변경 사항은 WHERE x.TableAID = a.ID 또는 x.TableAID가 null에서 ON x.TableAID = a.ID입니다. OUTER 조인에서 필터 위치를 변경하면 컴파일러가 조인 후 필터가 아닌 필터 후 조인을 알 수 있습니다. 또한 Null과 일치 할 필요가 없기 때문에 해당 열에서 인덱스를 사용할 수있었습니다. 쿼리 응답이 61 초에서 2 초로 변경되었습니다.
Ben Gripka

10

두 가지 방법이있는 한.

  • JOIN / ON은 테이블 결합을위한 것입니다.
  • 결과 필터링을위한 WHERE

당신은 그것들을 다르게 사용할 수 있지만 그것은 항상 나에게 냄새처럼 보입니다.

문제가있을 때 성능을 처리하십시오. 그런 다음 이러한 "최적화"를 살펴볼 수 있습니다.


2

어떤 쿼리 옵티마이 저가 센트로 .... 그들은 동일합니다.


실제 작업량에 따라 동일하지 않다고 확신합니다. 데이터가 거의 없다면 그 질문은 쓸모가 없습니다.
eKek0 2010 년

2
실제 작업 부하에서 확인하십시오. 기본적으로-동일한 실행 계획을 생성하면 성능이 동일합니다. 적어도 정상 / 간단한 경우 (즉, 14 개의 테이블을 결합하는 경우가 아님) 나는 그것들이 동일하다고 확신합니다.)
TomTom

1

postgresql에서는 동일합니다. explain analyze각 쿼리에 대해 수행 하면 계획이 동일하게 나오기 때문에 이것을 알고 있습니다. 이 예를 보자 :

# explain analyze select e.* from event e join result r on e.id = r.event_id and r.team_2_score=24;

                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=27.09..38.22 rows=7 width=899) (actual time=0.045..0.047 rows=1 loops=1)
   Hash Cond: (e.id = r.event_id)
   ->  Seq Scan on event e  (cost=0.00..10.80 rows=80 width=899) (actual time=0.009..0.010 rows=2 loops=1)
   ->  Hash  (cost=27.00..27.00 rows=7 width=8) (actual time=0.017..0.017 rows=1 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on result r  (cost=0.00..27.00 rows=7 width=8) (actual time=0.006..0.008 rows=1 loops=1)
               Filter: (team_2_score = 24)
               Rows Removed by Filter: 1
 Planning time: 0.182 ms
 Execution time: 0.101 ms
(10 rows)

# explain analyze select e.* from event e join result r on e.id = r.event_id where r.team_2_score=24;
                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=27.09..38.22 rows=7 width=899) (actual time=0.027..0.029 rows=1 loops=1)
   Hash Cond: (e.id = r.event_id)
   ->  Seq Scan on event e  (cost=0.00..10.80 rows=80 width=899) (actual time=0.010..0.011 rows=2 loops=1)
   ->  Hash  (cost=27.00..27.00 rows=7 width=8) (actual time=0.010..0.010 rows=1 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on result r  (cost=0.00..27.00 rows=7 width=8) (actual time=0.006..0.007 rows=1 loops=1)
               Filter: (team_2_score = 24)
               Rows Removed by Filter: 1
 Planning time: 0.140 ms
 Execution time: 0.058 ms
(10 rows)

둘 다 동일한 최소 및 최대 비용과 동일한 쿼리 계획을 갖습니다. 또한 최상위 쿼리에서도 team_score_2가 '필터'로 적용됩니다.


0

이 조인의 배치가 성능을 결정하는 요소가 될 가능성은 거의 없습니다. 나는 tsql의 실행 계획에 대해 잘 알지 못하지만 유사한 계획에 자동으로 최적화 될 가능성이 높습니다.


0

규칙 # 0 : 벤치 마크를 실행하고보십시오! 어느 것이 더 빠를 지 실제로 알 수있는 유일한 방법은 그것을 시도하는 것입니다. 이러한 유형의 벤치 마크는 SQL 프로파일 러를 사용하여 수행하기가 매우 쉽습니다.

또한 JOIN 및 WHERE 절로 작성된 쿼리의 실행 계획을 조사하여 어떤 차이점이 두드러 지는지 확인하십시오.

마지막으로, 다른 사람들이 말했듯이이 두 가지는 SQL Server에 내장 된 최적화 프로그램을 포함하여 괜찮은 최적화 프로그램에 의해 동일하게 처리되어야합니다.


그러나 내부 조인에만 해당됩니다. 결과 세트는 아웃 조인에 대해 매우 다릅니다.
HLGEM 2010 년

물론이야. 다행히 제공된 예제에서는 내부 조인을 사용합니다.
3Dave 2010 년

1
안타깝게도 문제는 내부 조인이 아니라 조인에 관한 것입니다.
Paul

예 David, 질문은 조인에 관한 것입니다. 질문을 지원하는 샘플은 내부 조인을 사용합니다.

0

더 빠릅니까? 그것을 시도하고보십시오.

어느 것이 더 읽기 쉽습니까? 이동 된 조건이 실제로 조인과 관련이 없기 때문에 나에게 첫 번째는 더 "정확한"것처럼 보입니다.


0

데이터에 대해 더 구체적인 필터를 만들기 때문에 첫 번째라고 생각합니다. 그러나 데이터, 서버 하드웨어 등의 크기에 따라 매우 다를 수 있으므로 모든 최적화와 마찬가지로 실행 계획을 확인해야합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.