Porównaj te 2 zapytania. Czy szybciej jest umieścić filtr na kryteriach łączenia, czy w WHERE
klauzuli. Zawsze czułem, że jest szybszy na kryteriach łączenia, ponieważ zmniejsza zestaw wyników w możliwie najszybszym momencie, ale nie wiem na pewno.
Zamierzam zbudować kilka testów do obejrzenia, ale chciałem też uzyskać opinie, które byłyby bardziej czytelne.
Zapytanie 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? */
Zapytanie 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
EDYTOWAĆ
Przeprowadziłem kilka testów i wyniki pokazują, że faktycznie jest bardzo blisko, ale WHERE
klauzula jest faktycznie nieco szybsza! =)
Absolutnie zgadzam się, że bardziej sensowne jest zastosowanie filtru w WHERE
klauzuli, byłem po prostu ciekawy konsekwencji wydajności.
CZAS OPRACOWANY, GDZIE KRYTERIA: 143016 ms
CZAS OPRACOWANY DOŁĄCZ KRYTERIA: 143256 ms
TEST
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
sql
sql-server
tsql
sql-server-2008
Jon Erickson
źródło
źródło
Odpowiedzi:
Pod względem wydajności są takie same (i produkują te same plany)
Logicznie rzecz biorąc, trzeba zrobić operację, która nadal ma sens, jeśli zastąpi
INNER JOIN
zLEFT JOIN
.W twoim przypadku będzie to wyglądać tak:
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
albo to:
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
Pierwsze zapytanie nie zwróci żadnych rzeczywistych dopasowań dla
a.id
innego niż1
, więc druga składnia (zWHERE
) jest logicznie bardziej spójna.źródło
a.id = 1
dotyczy tylko skrzyżowania, a nie lewej części wyłączającej skrzyżowanie.a.id != 1
, w drugim będą tylko wiersze, w którycha.id = 1
.W przypadku złączeń wewnętrznych nie ma znaczenia, gdzie umieścisz swoje kryteria. Kompilator SQL przekształci oba w plan wykonania, w którym filtrowanie odbywa się poniżej złączenia (tj. Tak, jakby wyrażenia filtru znajdowały się w warunku złączenia).
Sprzężenia zewnętrzne to inna sprawa, ponieważ miejsce filtru zmienia semantykę zapytania.
źródło
Jeśli chodzi o dwie metody.
Chociaż możesz ich używać inaczej, zawsze wydaje mi się to zapachem.
Zajmij się wydajnością, gdy jest to problem. Następnie możesz przyjrzeć się takim „optymalizacjom”.
źródło
Z każdym optymalizatorem zapytań będzie wart cent ... są identyczne.
źródło
W postgresql są takie same. Wiemy o tym, ponieważ jeśli zrobisz
explain analyze
na każdym z zapytań, plan będzie taki sam. Weź ten przykład:# 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)
Oba mają ten sam minimalny i maksymalny koszt, a także ten sam plan zapytań. Zwróć także uwagę, że nawet w najwyższym zapytaniu team_score_2 jest stosowany jako „Filtr”.
źródło
Jest naprawdę mało prawdopodobne, aby umieszczenie tego połączenia miało decydujący wpływ na wydajność. Nie jestem dokładnie zaznajomiony z planowaniem wykonania dla tsql, ale prawdopodobnie zostaną one automatycznie zoptymalizowane do podobnych planów.
źródło
Zasada # 0: Przeprowadź testy porównawcze i zobacz! Jedynym sposobem, aby naprawdę stwierdzić, co będzie szybsze, jest wypróbowanie tego. Tego typu testy porównawcze są bardzo łatwe do wykonania przy użyciu profilera SQL.
Przeanalizuj również plan wykonania zapytania napisanego za pomocą klauzuli JOIN i WHERE, aby zobaczyć, jakie różnice się wyróżniają.
Wreszcie, jak powiedzieli inni, te dwa powinny być traktowane identycznie przez każdy przyzwoity optymalizator, w tym ten wbudowany w SQL Server.
źródło
Czy to jest szybsze? Spróbuj i zobacz.
Który jest łatwiejszy do odczytania? Pierwszy wydaje mi się bardziej „poprawny”, ponieważ przeniesiony warunek nie ma nic wspólnego ze złączeniem.
źródło
Myślę, że to pierwsze, ponieważ tworzy bardziej szczegółowy filtr danych. Ale powinieneś zobaczyć plan wykonania , jak w przypadku każdej optymalizacji, ponieważ może się on bardzo różnić w zależności od rozmiaru danych, sprzętu serwera itp.
źródło