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

이 두 쿼리를 비교하십시오. 조인 기준이나 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



답변

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

논리적으로, 당신은 당신이 대체 할 경우 여전히 감각이 작동해야 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)은 논리적으로 더 일관성이 있습니다.


답변

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

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


답변

두 가지 방법이있는 한.

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

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

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


답변

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


답변

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가 ‘필터’로 적용됩니다.


답변

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


답변

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

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

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