SQL: come verificare se esiste un record


207

Durante la lettura della documentazione relativa alla sintonizzazione SQL, ho trovato questo:

SELECT COUNT(*) :

  • Conta il numero di righe.
  • Spesso viene utilizzato in modo errato per verificare l'esistenza di un record.

È SELECT COUNT(*)davvero così male?

Qual è il modo corretto per verificare l'esistenza di un record?

Risposte:


255

È meglio utilizzare uno dei seguenti:

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

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

La prima alternativa non dovrebbe dare alcun risultato o un risultato, il secondo conteggio dovrebbe essere zero o uno.

Quanti anni ha la documentazione che stai utilizzando? Sebbene tu abbia letto buoni consigli, la maggior parte degli ottimizzatori di query negli ultimi RDBMS ottimizzano SELECT COUNT(*)comunque, quindi mentre c'è una differenza nella teoria (e nei database più vecchi), non dovresti notare alcuna differenza nella pratica.


1
Chiarirò che intendevo "chiave unica" con la clausola "chiave = valore", ma a parte questo sono ancora dietro la mia risposta.
Martin Schapendonk,

1
OK. Con tale premessa, infatti, la query restituirebbe solo uno o zero record. MA: la domanda non si limita a una colonna univoca. Inoltre: il secondo conteggio delle query (1) equivale al conteggio (*) da un POV pratico.
Martin Ba,

1
La domanda dice "qual è il modo corretto di verificare l'esistenza di un record". L'ho interpretato singolarmente, come in: 1 disco. La differenza tra count (*) e count (1) è già coperta dalla mia risposta. Preferisco contare (1) perché non si basa su un'implementazione RDBMS specifica.
Martin Schapendonk,

192

Preferirei non usare affatto la funzione Count:

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

Ad esempio, se si desidera verificare se l'utente esiste prima di inserirlo nel database, la query può apparire così:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

Generalmente lo usiamo (la verifica) quando vogliamo fare qualcosa, quindi la tua risposta è più completa.
Abner Escócio

Buono a
dirlo

20

Puoi usare:

SELECT 1 FROM MyTable WHERE <MyCondition>

Se non esiste alcun record corrispondente alla condizione, il recordset risultante è vuoto.


Intendevi TOP 1? -> (SELEZIONA LA TOP 1 DA MyTable DOVE <MyCondition>)
Jacob

6
No, intendevo esattamente "1"
Cătălin Pitiș,

1
per abilitare lo Strumento per ottimizzare le query anche se non leggerai / non avrai bisogno dei set di dati rimanenti, dovresti indicare SELEZIONA TOP 1 1 DA ... DOVE ... (o usa i suggerimenti di query appropriati per il tuo RDBS)
eFloh

3
L'operatore Exists stesso tenta di recuperare solo il minimo assoluto di informazioni, quindi l'aggiunta di TOP 1 non fa altro che aggiungere 5 caratteri alla dimensione della query. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex

13

Le altre risposte sono abbastanza buone, ma sarebbe anche utile aggiungere LIMIT 1(o l'equivalente , per impedire il controllo di righe non necessarie.


3
Se una query "verifica l'esistenza" restituisce più di una riga, penso che sia più utile ricontrollare la clausola WHERE anziché limitare il numero di risultati.
Martin Schapendonk,

2
Penso che Limit sia usato in Oracle e non in SQL Server
Shantanu Gupta,

7
Sto considerando il caso in cui possono legittimamente essere più righe - in cui la domanda è: "Esistono (una o più) righe che soddisfano questa condizione?" In tal caso, non vuoi guardarli tutti, solo uno.
JesseW,

1
@Shantanu - Lo so, è per questo che mi sono collegato all'articolo (molto attraverso) en.wikipedia che spiega le altre forme.
JesseW,

11
SELECT COUNT(1) FROM MyTable WHERE ...

passerà attraverso tutti i record. Questo è il motivo per cui è male da usare per l'esistenza record.

io userei

SELECT TOP 1 * FROM MyTable WHERE ...

Dopo aver trovato 1 record, terminerà il loop.


Nel caso in cui SELECT TOP 1terminerà effettivamente dopo averne trovato uno o continuerà a trovare tutto per poter dire quale è TOP?
Eirik H,

3
PS: Sicuramente ho sempreIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

l'operatore Stella imporrà al DBMS di accedere all'indice cluster anziché solo agli indici necessari per la condizione di join. quindi è meglio usare un valore costante come risultato, cioè selezionare il primo 1 1 .... Ciò restituirà 1 o DB-Null, a seconda della condizione è una corrispondenza o meno.
eFloh,

è carino. Mi piace il primo.
Isxaker,

10

Puoi usare:

SELECT COUNT(1) FROM MyTable WHERE ... 

o

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

Questo sarà più efficiente rispetto a SELECT *quando stai semplicemente selezionando il valore 1 per ogni riga, piuttosto che tutti i campi.

C'è anche una sottile differenza tra COUNT (*) e COUNT (nome colonna):

  • COUNT(*) conterà tutte le righe, inclusi i valori null
  • COUNT(column name)conterà solo le occorrenze non nulle del nome della colonna

2
Stai assumendo erroneamente che un DBMS controllerà in qualche modo tutte quelle colonne. La differenza di prestazioni tra count(1)e count(*)sarà diversa solo nel DBMS più cerebrale.
paxdiablo,

2
No, sto dicendo che in realtà fai affidamento sui dettagli di implementazione quando affermi che sarà più efficiente. Se vuoi davvero assicurarti di ottenere le migliori prestazioni, dovresti profilarle per l'implementazione specifica usando dati rappresentativi o dimenticartene totalmente. Qualsiasi altra cosa è potenzialmente fuorviante e potrebbe cambiare drasticamente quando si sposta (ad esempio) da DB2 a MySQL.
paxdiablo,

1
Voglio chiarire che non sto insinuando la tua risposta. Si è utile. L'unico bit con cui mi metto in discussione è la dichiarazione di efficienza poiché abbiamo fatto delle valutazioni in DB2 / z e abbiamo scoperto che non c'è alcuna differenza reale tra count(*)e count(1). Se questo è il caso di altri DBMS ', non posso dire.
paxdiablo,

3
"Qualsiasi altra cosa è potenzialmente fuorviante e potrebbe cambiare drasticamente quando ci si sposta (ad esempio) da DB2 a MySQL" È molto più probabile che si venga morsi dalla degradazione delle prestazioni di SELECT COUNT (*) quando si sposta DBMS rispetto a una differenza di implementazione in SELECT 1 o COUNT (1). Sono fermamente convinto a scrivere il codice che esprime più chiaramente esattamente ciò che si desidera ottenere, piuttosto che fare affidamento su ottimizzatori o compilatori per impostazione predefinita sul comportamento desiderato.
Winston Smith,

1
L'istruzione fuorviante "COUNT (*)" significa "contare il punto" delle righe. Non richiede l'accesso a nessuna colonna particolare. E nella maggior parte dei casi non sarà nemmeno necessario accedere alla riga stessa poiché è sufficiente un indice univoco.
James Anderson,

9

Puoi usare:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Utilizzare select 1per impedire il controllo di campi non necessari.

Utilizzare LIMIT 1 per impedire il controllo di righe non necessarie.


3
Un buon punto, ma Limit funziona su MySQL e PostgreSQL, i migliori funzionano su SQL Server, dovresti prenderne nota sulla tua risposta
Leo Gurdian,

0

Sto usando in questo modo:

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

0

Altra opzione:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.