![](/img/trans.png)
[英]SQL Filter criteria in join criteria or where clause which is more efficient
[英]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 編譯器會將兩者都轉換為一個執行計划,其中過濾發生在連接下方(即,過濾表達式出現在連接條件中)。
外連接是另一回事,因為過濾器的位置會改變查詢的語義。
就這兩種方法而言。
雖然你可以用不同的方式使用它們,但對我來說似乎總是一種氣味。
遇到問題時處理性能問題。 然后你可以研究這樣的“優化”。
對於任何值一分錢的查詢優化器......它們是相同的。
在 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.