Какой SQL-запрос выполняется быстрее? Фильтровать по критерию Присоединиться или Где?
Сравните эти 2 запроса. Быстрее ли устанавливать фильтр на критерии соединения или в предложение. Я всегда чувствовал, что это быстрее по критериям соединения, потому что он уменьшает набор результатов в самый скорый момент, но я не знаю точно.
Я собираюсь собрать некоторые тесты, но я также хотел получить мнения, по которым было бы более понятным читать.
Запрос 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 ms
ЭЛЕМЕНТЫ ВРЕМЕНИ ПРИСОЕДИНЯЮТСЯ КРИТЕРИИ: 143256 мс
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
Ответы
Ответ 1
По производительности, они одинаковы (и производят одни и те же планы)
Логически, вы должны сделать операцию, которая все еще имеет смысл, если вы замените 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
) логически более согласован.
Ответ 2
Для внутренних объединений не имеет значения, где вы ставите свои критерии. Компилятор SQL преобразует оба в план выполнения, в котором фильтрация происходит под соединением (т.е., как если бы выражения фильтра отображались в условии соединения).
Внешние соединения - это другое дело, так как место фильтра изменяет семантику запроса.
Ответ 3
Что касается двух методов:
- JOIN/ON предназначен для соединения таблиц
- ГДЕ для фильтрации результатов
Пока вы можете использовать их по-разному, это всегда кажется мне запахом.
Относитесь к производительности, когда это проблема. Затем вы можете посмотреть на такие "оптимизации".
Ответ 4
С любым оптимизатором запросов worh cent... они идентичны.
Ответ 5
Я предполагаю, что первый, потому что он делает более конкретный фильтр по данным. Но вы должны видеть план выполнения, как и при любой оптимизации, потому что он может быть очень разным, зависящим от размера данных, серверного оборудования и т.д.
Ответ 6
Это быстрее? Попробуйте и посмотрите.
Что легче читать? Первый для меня выглядит более "правильным", поскольку перемещенное условие не имеет ничего общего с соединением.
Ответ 7
На самом деле маловероятно, что размещение этого соединения будет решающим фактором производительности. Я не очень хорошо знаком с планированием выполнения для tsql, но, скорее всего, они будут автоматически оптимизированы для подобных планов.
Ответ 8
Правило № 0: Запустите некоторые тесты и посмотрите! Единственный способ узнать, что будет быстрее - попробовать. Эти типы тестов очень просты в использовании с использованием профилировщика SQL.
Также рассмотрите план выполнения запроса, написанного с помощью JOIN, и с предложением WHERE, чтобы узнать, какие отличия отличаются.
Наконец, как говорили другие, эти два должны обрабатываться одинаково любым достойным оптимизатором, в том числе встроенным в SQL Server.