SQL: Cara memeriksa dengan benar apakah ada catatan

207

Saat membaca beberapa dokumentasi terkait SQL Tuning, saya menemukan ini:

SELECT COUNT(*) :

  • Menghitung jumlah baris.
  • Seringkali tidak tepat digunakan untuk memverifikasi keberadaan catatan.

Benarkah SELECT COUNT(*)itu buruk?

Apa cara yang tepat untuk memverifikasi keberadaan catatan?

systempuntoout
sumber

Jawaban:

253

Lebih baik menggunakan salah satu dari berikut ini:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

Alternatif pertama seharusnya tidak memberi Anda hasil atau satu hasil, hitungan kedua harus nol atau satu.

Berapa umur dokumentasi yang Anda gunakan? Meskipun Anda telah membaca saran yang bagus, sebagian besar pengoptimal kueri dalam optimisasi RDBMS baru-baru ini SELECT COUNT(*), jadi meskipun ada perbedaan dalam teori (dan database yang lebih lama), Anda seharusnya tidak melihat adanya perbedaan dalam praktiknya.

Martin Schapendonk
sumber
1
Saya akan mengklarifikasi bahwa saya bermaksud "kunci unik" dengan klausa "kunci = nilai" tetapi selain itu saya masih di belakang jawaban saya.
Martin Schapendonk
1
BAIK. Dengan premis itu memang permintaan akan mengembalikan hanya satu atau nol catatan. TAPI: Pertanyaannya tidak terbatas pada kolom yang unik. Juga: Hitungan kueri ke-2 (1) sama dengan menghitung (*) dari POV praktis.
Martin Ba
1
Pertanyaannya mengatakan "apa cara yang tepat untuk memverifikasi keberadaan catatan A". Saya menafsirkannya sebagai tunggal, seperti dalam: 1 catatan. Perbedaan antara jumlah (*) dan jumlah (1) sudah tercakup oleh jawaban saya. Saya lebih suka menghitung (1) karena tidak bergantung pada implementasi RDBMS tertentu.
Martin Schapendonk
192

Saya lebih suka tidak menggunakan fungsi Hitung sama sekali:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

Misalnya jika Anda ingin memeriksa apakah pengguna ada sebelum memasukkannya ke dalam database, kueri akan terlihat seperti ini:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END
Pavel Morshenyuk
sumber
Secara umum kami menggunakannya (verifikasi) ketika ingin melakukan sesuatu, maka jawaban Anda lebih lengkap.
Abner Escócio
Senang menyebutkan bahwa dengan menggunakan T-SQL
Bronek
20

Kamu bisa memakai:

SELECT 1 FROM MyTable WHERE <MyCondition>

Jika tidak ada catatan yang cocok dengan kondisi, recordset yang dihasilkan kosong.

Cătălin Pitiș
sumber
Apakah maksud Anda TOP 1? -> (PILIH TOP 1 DARI MyTable WHERE <MyCondition>)
Jacob
6
Tidak, maksud saya persis "1"
Cătălin Pitiș
1
untuk mengaktifkan pengoptimal kueri untuk mengetahui bahwa Anda tidak akan membaca / memerlukan set data yang tersisa, Anda harus menyatakan PILIH TOP 1 1 DARI ... DI MANA ... (atau gunakan petunjuk permintaan yang sesuai untuk RDBS Anda)
eFloh
3
Operator yang ada itu sendiri mencoba mengambil hanya informasi minimum absolut, sehingga penambahan TOP 1 tidak melakukan apa-apa selain menambahkan 5 karakter ke ukuran kueri. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex
13

Jawaban lainnya cukup bagus, tetapi akan berguna juga untuk menambahkan LIMIT 1(atau yang setara , untuk mencegah pengecekan baris yang tidak perlu.

JesseW
sumber
3
Jika ada permintaan "periksa keberadaan" mengembalikan lebih dari satu baris, saya pikir akan lebih bermanfaat untuk memeriksa ulang klausa WHERE Anda alih-alih LIMIT-ing jumlah hasil.
Martin Schapendonk
2
Saya pikir Limit digunakan di Oracle dan bukan di SQL Server
Shantanu Gupta
7
Saya mempertimbangkan kasus di mana mereka dapat secara sah menjadi beberapa baris - di mana pertanyaannya adalah: "Apakah ada (satu atau lebih) baris yang memenuhi kondisi ini?" Dalam hal ini, Anda tidak ingin melihat semuanya, hanya satu.
JesseW
1
@ Shantanu - Saya tahu, itu sebabnya saya menautkan ke artikel en.wikipedia (sangat lengkap) yang menjelaskan bentuk lain.
JesseW
11
SELECT COUNT(1) FROM MyTable WHERE ...

akan mengulang melalui semua catatan. Ini adalah alasan yang buruk untuk digunakan untuk keberadaan rekaman.

Saya akan menggunakan

SELECT TOP 1 * FROM MyTable WHERE ...

Setelah menemukan 1 record, itu akan mengakhiri loop.

oski
sumber
Dalam hal SELECT TOP 1apakah itu benar-benar berakhir setelah menemukan satu atau apakah ia terus menemukan semua untuk dapat mengatakan mana yang TOP?
Eirik H
3
PS: IF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Pasti
operator Star akan memaksa DBMS untuk mengakses indeks yang dikelompokkan bukan hanya indeks yang akan diperlukan untuk kondisi bergabung Anda. jadi lebih baik menggunakan valua konstan sebagai hasilnya, yaitu pilih top 1 1 .... Itu akan mengembalikan 1 atau DB-Null, tergantung pada kondisinya apakah cocok atau tidak.
eFloh
ini bagus. Saya suka yang pertama.
Isxaker
10

Kamu bisa memakai:

SELECT COUNT(1) FROM MyTable WHERE ... 

atau

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

Ini akan lebih efisien daripada SELECT *karena Anda hanya memilih nilai 1 untuk setiap baris, daripada semua bidang.

Ada juga perbedaan tipis antara COUNT (*) dan COUNT (nama kolom):

  • COUNT(*) akan menghitung semua baris, termasuk nol
  • COUNT(column name)hanya akan menghitung kemunculan nama kolom yang bukan nol
Winston Smith
sumber
2
Anda membuat asumsi yang salah bahwa DBMS akan memeriksa semua kolom tersebut. Perbedaan kinerja antara count(1)dan count(*)akan berbeda hanya di DBMS yang paling mati otak.
paxdiablo
2
Tidak, saya mengatakan bahwa Anda benar-benar mengandalkan detail implementasi ketika Anda menyatakan itu akan lebih efisien. Jika Anda benar-benar ingin memastikan Anda mendapatkan kinerja terbaik, Anda harus membuat profil untuk implementasi spesifik menggunakan data representatif, atau lupakan saja. Hal lain yang berpotensi menyesatkan, dan dapat berubah secara drastis saat memindahkan (misalnya) dari DB2 ke MySQL.
paxdiablo
1
Saya ingin menjelaskan bahwa saya tidak menolak jawaban Anda. Ini adalah berguna. Satu-satunya hal yang saya ambil masalah adalah klaim efisiensi karena kami telah melakukan evaluasi dalam DB2 / z dan menemukan tidak ada perbedaan nyata antara count(*)dan count(1). Apakah itu yang terjadi pada DBMS lain , saya tidak bisa mengatakannya.
paxdiablo
3
"Ada hal lain yang berpotensi menyesatkan, dan dapat berubah secara drastis saat memindahkan (misalnya) dari DB2 ke MySQL" Anda jauh lebih mungkin tergigit oleh penurunan kinerja SELECT COUNT (*) saat memindahkan DBMS daripada perbedaan implementasi di SELECT 1 atau COUNT (1). Saya sangat percaya dalam menulis kode yang paling jelas mengungkapkan apa yang ingin Anda capai, daripada mengandalkan pengoptimal atau kompiler untuk default ke perilaku yang Anda inginkan.
Winston Smith
1
Pernyataan menyesatkan "COUNT (*)" berarti berhenti total baris. Itu tidak memerlukan akses ke kolom tertentu. Dan dalam kebanyakan kasus bahkan tidak akan memerlukan akses ke baris itu sendiri karena hitungan indeks unik apa pun sudah cukup.
James Anderson
9

Kamu bisa memakai:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Gunakan select 1untuk mencegah pengecekan bidang yang tidak perlu.

Gunakan LIMIT 1 untuk mencegah pemeriksaan baris yang tidak perlu.

pengguna3059943
sumber
3
Poin bagus tetapi Limit berfungsi di MySQL dan PostgreSQL, pekerjaan terbaik di SQL Server, Anda harus mencatatnya pada jawaban Anda
Leo Gurdian
0

Saya menggunakan cara ini:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist
DiPix
sumber
0

Pilihan lain:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Pranav
sumber