Сравните эти 2 запроса. Быстрее поставить фильтр по критериям соединения или в 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
) логически более согласован.источник
a.id = 1
применяется только для пересечения, а не для левой части, за исключением пересечения.a.id != 1
, в другом - только строки wherea.id = 1
.Для внутренних объединений не имеет значения, где вы ставите свои критерии. Компилятор 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.
источник
Это быстрее? Попробуйте и убедитесь.
Что легче читать? Первый вариант мне кажется более «правильным», поскольку перемещенное условие не имеет ничего общего с объединением.
источник
Я предполагаю, что первый, потому что он делает более конкретный фильтр данных. Но вы должны увидеть план выполнения , как и при любой оптимизации, потому что он может сильно отличаться в зависимости от размера данных, серверного оборудования и т. Д.
источник