Setiap kali saya perlu memeriksa keberadaan beberapa baris dalam sebuah tabel, saya cenderung selalu menulis kondisi seperti:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Beberapa orang lain menulisnya seperti:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Ketika kondisinya NOT EXISTS
bukan EXISTS
: Dalam beberapa kesempatan, saya mungkin menulisnya dengan LEFT JOIN
dan dan kondisi tambahan (kadang-kadang disebut antijoin ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
Saya mencoba menghindarinya karena saya pikir artinya kurang jelas, khususnya ketika apa yang Anda primary_key
tidak jelas, atau ketika kunci utama Anda atau kondisi gabungan Anda adalah multi-kolom (dan Anda dapat dengan mudah melupakan salah satu kolom). Namun, kadang-kadang Anda mempertahankan kode yang ditulis oleh orang lain ... dan itu ada di sana.
Apakah ada perbedaan (selain gaya) untuk digunakan,
SELECT 1
bukanSELECT *
?
Apakah ada sudut yang tidak berperilaku sama?Meskipun apa yang saya tulis adalah (AFAIK) SQL standar: Apakah ada perbedaan untuk database yang berbeda / versi yang lebih lama?
Apakah ada keuntungan dari kejujuran menulis antijoin?
Apakah perencana / pengoptimal kontemporer memperlakukannya secara berbeda dariNOT EXISTS
klausa?
sumber
EXISTS (SELECT FROM ...)
.Jawaban:
Tidak, tidak ada perbedaan efisiensi antara
(NOT) EXISTS (SELECT 1 ...)
dan(NOT) EXISTS (SELECT * ...)
di semua DBMS utama. Saya sering melihat(NOT) EXISTS (SELECT NULL ...)
digunakan juga.Dalam beberapa Anda bahkan dapat menulis
(NOT) EXISTS (SELECT 1/0 ...)
dan hasilnya sama - tanpa kesalahan (pembagian dengan nol), yang membuktikan bahwa ekspresi di sana bahkan tidak dievaluasi.Tentang
LEFT JOIN / IS NULL
metode antijoin, koreksi: ini setara denganNOT EXISTS (SELECT ...)
.Dalam hal ini,
NOT EXISTS
vs.LEFT JOIN / IS NULL
, Anda mungkin mendapatkan rencana eksekusi yang berbeda. Dalam MySQL misalnya dan sebagian besar dalam versi yang lebih lama (sebelum 5.7) rencana akan sangat mirip tetapi tidak identik. Pengoptimal dari DBMS lain (SQL Server, Oracle, Postgres, DB2) adalah - sejauh yang saya tahu - lebih atau kurang mampu menulis ulang 2 metode ini dan mempertimbangkan rencana yang sama untuk keduanya. Namun, tidak ada jaminan seperti itu dan ketika melakukan optimasi, ada baiknya untuk memeriksa rencana dari penulisan ulang ekuivalen yang berbeda karena mungkin ada kasus-kasus yang tidak ditulis ulang oleh setiap pengoptimal (mis. Kueri kompleks, dengan banyak gabungan dan / atau tabel turunan / subqueries di dalam subquery, di mana kondisi dari beberapa tabel, kolom komposit yang digunakan dalam kondisi penggabungan) atau pilihan dan rencana pengoptimal dipengaruhi secara berbeda oleh indeks, pengaturan, dll.Perhatikan juga bahwa
USING
tidak dapat digunakan di semua DBMS (SQL Server misalnya). Semakin umumJOIN ... ON
bekerja di mana-mana.Dan kolom harus diawali dengan nama tabel / alias di
SELECT
untuk menghindari kesalahan / ambiguitas ketika kita telah bergabung.Saya juga biasanya lebih suka untuk memasukkan kolom yang bergabung dalam
IS NULL
cek (walaupun PK atau kolom yang tidak dapat dibatalkan akan OK, mungkin berguna untuk efisiensi ketika rencana untukLEFT JOIN
menggunakan indeks non-clustered):Ada juga metode ketiga untuk antijoin, menggunakan
NOT IN
tetapi ini memiliki semantik yang berbeda (dan hasilnya!) Jika kolom tabel di dalam nullable. Itu dapat digunakan meskipun dengan mengecualikan baris denganNULL
, membuat kueri setara dengan 2 versi sebelumnya:Ini juga biasanya menghasilkan rencana serupa di sebagian besar DBMS.
sumber
[NOT] IN (SELECT ...)
, meskipun setara, berkinerja sangat buruk. Hindari itu!SELECT *
tentu melakukan lebih banyak pekerjaan. Demi kesederhanaan, saya sarankan menggunakanSELECT 1
Ada satu kategori kasus di mana
SELECT 1
danSELECT *
tidak dapat dipertukarkan - lebih khusus, satu akan selalu diterima dalam kasus-kasus tersebut sementara yang lain sebagian besar tidak akan.Saya berbicara tentang kasus di mana Anda perlu memeriksa keberadaan barisan kumpulan yang dikelompokkan . Jika tabel
T
memiliki kolomC1
danC2
dan Anda memeriksa keberadaan grup baris yang cocok dengan kondisi tertentu, Anda dapat menggunakanSELECT 1
seperti ini:tetapi Anda tidak dapat menggunakan
SELECT *
dengan cara yang sama.Itu hanyalah aspek sintaksis. Jika kedua opsi diterima secara sintaksis, kemungkinan besar Anda tidak akan memiliki perbedaan dalam hal kinerja atau hasil yang dikembalikan, seperti yang telah dijelaskan dalam jawaban lainnya .
Catatan tambahan mengikuti komentar
Tampaknya tidak banyak produk database yang benar-benar mendukung perbedaan ini. Produk seperti SQL Server, Oracle, MySQL dan SQLite akan dengan senang hati menerima
SELECT *
permintaan di atas tanpa kesalahan, yang mungkin berarti mereka memperlakukan EXISTSSELECT
dengan cara khusus.PostgreSQL adalah salah satu RDBMS yang
SELECT *
mungkin gagal, tetapi masih dapat berfungsi dalam beberapa kasus. Secara khusus, jika Anda dikelompokkan oleh PK,SELECT *
akan berfungsi dengan baik, jika tidak maka akan gagal dengan pesan:sumber
GROUP BY
, konsep*
tidak berarti (atau, setidaknya, tidak begitu jelas).Cara yang bisa dibilang menarik untuk menulis ulang
EXISTS
klausa yang menghasilkan permintaan yang lebih bersih, dan mungkin kurang menyesatkan, setidaknya dalam SQL Server adalah:Versi anti-semi-gabung itu akan terlihat seperti:
Keduanya biasanya dioptimalkan untuk rencana yang sama dengan
WHERE EXISTS
atauWHERE NOT EXISTS
, tetapi maksudnya tidak salah lagi, dan Anda tidak memiliki "aneh"1
atau*
.Menariknya, masalah cek nol yang terkait dengannya
NOT IN (...)
bermasalah untuk<> ALL (...)
, sedangkan yangNOT EXISTS (...)
tidak menderita dari masalah itu. Pertimbangkan dua tabel berikut dengan kolom nullable:Kami akan menambahkan beberapa data ke keduanya, dengan beberapa baris yang cocok, dan beberapa yang tidak:
The
NOT IN (...)
query:Memiliki rencana berikut:
Kueri tidak mengembalikan baris karena nilai NULL membuat persamaan tidak mungkin untuk dikonfirmasi.
Kueri ini, dengan
<> ALL (...)
memperlihatkan paket yang sama dan tidak mengembalikan baris:Varian menggunakan
NOT EXISTS (...)
, menunjukkan bentuk rencana yang sedikit berbeda, dan tidak mengembalikan baris:Rencana:
Hasil kueri itu:
Ini membuat penggunaan
<> ALL (...)
sama rentan terhadap hasil bermasalah sepertiNOT IN (...)
.sumber
*
aneh: Saya membacaEXISTS (SELECT * FROM t WHERE ...)
SAthere is a _row_ in table _t_ that...
. Bagaimanapun, saya suka punya alternatif, dan milik Anda jelas bisa dibaca. Satu keraguan / peringatan: bagaimana sikapnya jikab
nullable? [Saya punya pengalaman buruk dan beberapa malam pendek ketika mencoba mencari tahu kesalahan yang disebabkan olehx IN (SELECT something_nullable FROM a_table)
]"Bukti" bahwa mereka identik (dalam MySQL) harus dilakukan
lalu ulangi dengan
SELECT 1
. Dalam kedua kasus, output 'diperpanjang' menunjukkan bahwa itu diubah menjadiSELECT 1
.Demikian pula,
COUNT(*)
diubah menjadiCOUNT(0)
.Hal lain yang perlu diperhatikan: Peningkatan optimasi telah dibuat dalam versi terbaru. Mungkin ada baiknya membandingkan
EXISTS
vs anti-bergabung. Versi Anda dapat melakukan pekerjaan yang lebih baik dengan yang satu versus yang lain.sumber
Di beberapa database, optimasi ini belum berfungsi. Seperti misalnya di PostgreSQL Pada versi 9.6, ini akan gagal.
Dan ini akan berhasil.
Gagal karena yang berikut gagal tetapi itu masih berarti ada perbedaan.
Anda dapat menemukan informasi lebih lanjut tentang kekhasan khusus ini dan pelanggaran spesifikasi dalam jawaban saya atas pertanyaan, Apakah SQL Spec memerlukan GROUP BY dalam EXISTS ()
sumber
Saya selalu menggunakan
select top 1 'x'
(SQL Server)Secara teoritis,
select top 1 'x'
akan lebih efisien bahwaselect *
, karena yang pertama akan lengkap setelah memilih konstan pada keberadaan baris kualifikasi, sedangkan yang terakhir akan memilih semuanya.NAMUN, meskipun sangat awal mungkin relevan, optimisasi telah membuat perbedaan tidak relevan dalam semua RDBS utama.
sumber
top n
tanpaorder by
adalah ide yang baik.select top 1 'x'
seharusnya tidak lebih efisien daripadaselect *
dalamExist
ekspresi. Secara praktis mungkin lebih efisien jika optimizer berfungsi suboptimal tetapi secara teoritis kedua ekspresi tersebut setara.IF EXISTS(SELECT TOP(1) 1 FROM
adalah kebiasaan jangka panjang dan lintas platform yang lebih baik hanya karena Anda bahkan tidak perlu mulai khawatir tentang seberapa baik atau buruk platform / versi Anda saat ini; dan SQL bergerak dariTOP n
menuju parameterizableTOP(n)
. Ini harus menjadi keterampilan belajar-sekali.sumber
TOP
bahkan tidak valid SQL.TOP (n)
dalam "SQL" - bahasa permintaan standar. Ada satu di T-SQL yang merupakan dialek yang digunakan Microsoft SQL Server.