Bandingkan 2 kueri ini. Apakah lebih cepat menempatkan filter pada kriteria gabungan atau di WHERE
klausa. Saya selalu merasa lebih cepat pada kriteria join karena mengurangi hasil yang ditetapkan secepat mungkin, tapi saya tidak tahu pasti.
Saya akan membuat beberapa tes untuk dilihat, tetapi saya juga ingin mendapatkan pendapat tentang mana yang lebih jelas untuk dibaca juga.
Pertanyaan 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? */
Pertanyaan 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
EDIT
Saya menjalankan beberapa tes dan hasilnya menunjukkan bahwa sebenarnya sangat dekat, tetapi WHERE
klausulnya sebenarnya sedikit lebih cepat! =)
Saya sangat setuju bahwa lebih masuk akal untuk menerapkan filter pada WHERE
klausa, saya hanya ingin tahu tentang implikasi kinerjanya.
WAKTU TERLALU DI MANA KRITERIA: 143016 ms TERLALU
WAKTU BERGABUNG KRITERIA: 143256 ms
UJI
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
sumber
Jawaban:
Dari segi kinerja, mereka sama (dan menghasilkan rencana yang sama)
Logikanya, Anda harus membuat operasi yang masih masuk akal jika Anda mengganti
INNER JOIN
dengan fileLEFT JOIN
.Dalam kasus Anda ini akan terlihat seperti ini:
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
atau ini:
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
Kueri sebelumnya tidak akan mengembalikan kecocokan aktual apa pun
a.id
selain dari1
, jadi sintaksis terakhir (denganWHERE
) secara logis lebih konsisten.sumber
a.id = 1
hanya berlaku untuk persimpangan, bukan bagian kiri tidak termasuk persimpangan.a.id != 1
, yang lain hanya memiliki baris di manaa.id = 1
.Untuk gabungan dalam tidak masalah di mana Anda meletakkan kriteria Anda. Kompilator SQL akan mengubah keduanya menjadi rencana eksekusi di mana pemfilteran terjadi di bawah gabungan (mis. Seolah-olah ekspresi filter muncul dalam kondisi gabungan).
Gabungan luar adalah masalah yang berbeda, karena tempat filter mengubah semantik kueri.
sumber
Sejauh kedua metode berjalan.
Meskipun Anda dapat menggunakannya secara berbeda, itu selalu terasa seperti bau bagi saya.
Tangani kinerja saat itu menjadi masalah. Kemudian Anda dapat melihat "optimisasi" tersebut.
sumber
Dengan pengoptimal kueri apa pun bekerja satu sen .... mereka identik.
sumber
Di postgresql mereka sama. Kami tahu ini karena jika Anda melakukan
explain analyze
pada setiap kueri, rencananya akan sama. Ambil contoh ini:# 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)
Keduanya memiliki biaya min dan maks yang sama serta paket kueri yang sama. Juga, perhatikan bahwa bahkan di kueri teratas, team_score_2 akan diterapkan sebagai 'Filter'.
sumber
Sangat tidak mungkin bahwa penempatan sambungan ini akan menjadi faktor penentu kinerja. Saya tidak terlalu paham dengan perencanaan eksekusi untuk tsql, tetapi kemungkinan besar mereka akan dioptimalkan secara otomatis ke rencana serupa.
sumber
Aturan # 0: Jalankan beberapa tolok ukur dan lihat! Satu-satunya cara untuk benar-benar mengetahui mana yang lebih cepat adalah dengan mencobanya. Jenis tolok ukur ini sangat mudah dilakukan menggunakan SQL profiler.
Juga, periksa rencana eksekusi untuk kueri yang ditulis dengan JOIN dan dengan klausa WHERE untuk melihat perbedaan apa yang menonjol.
Akhirnya, seperti yang dikatakan orang lain, keduanya harus diperlakukan secara identik oleh pengoptimal yang layak, termasuk yang dibangun ke dalam SQL Server.
sumber
Apakah lebih cepat? Cobalah dan lihat.
Mana yang lebih mudah dibaca? Yang pertama bagi saya terlihat lebih "benar", karena kondisi pindah tidak ada hubungannya dengan sambungan.
sumber
Saya rasa itu yang pertama, karena itu membuat filter yang lebih spesifik atas data. Tetapi Anda harus melihat rencana eksekusi , seperti halnya pengoptimalan apa pun, karena ini bisa sangat berbeda bergantung pada ukuran data, perangkat keras server, dll.
sumber