簡體   English   中英

哪個 SQL 查詢更快? 篩選加入條件或 Where 子句?

[英]Which SQL query is faster? Filter on Join criteria or Where clause?

比較這兩個查詢。 將過濾器放在連接條件或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子句上應用過濾器更有意義,我只是對性能影響感到好奇。

標准經過的時間: 143016 毫秒
已用時間加入標准: 143256 毫秒

測試

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 ,因此后一個語法(使用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 中內置的優化器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM