Il modo più veloce per determinare se esiste un record


143

Come suggerisce il titolo ... Sto cercando di capire il modo più veloce con il minimo sovraccarico per determinare se esiste un record in una tabella o meno.

Query di esempio:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Dì che lo ?scambio è con 'TB100'... sia la prima che la seconda query restituiranno lo stesso esatto risultato (diciamo ... 1per questa conversazione). L'ultima query verrà restituita 'TB100'come previsto o nulla se idnon è presente nella tabella.

Lo scopo è capire se idè presente nella tabella o meno. In caso contrario, il programma inserirà successivamente il record, in caso affermativo, il programma lo salterà o eseguirà una query UPDATE basata su altra logica di programma al di fuori dell'ambito di questa domanda.

Qual è più veloce e ha meno spese generali? (Questo verrà ripetuto decine di migliaia di volte per ogni esecuzione del programma e verrà eseguito più volte al giorno).

(Esecuzione di questa query su M $ SQL Server da Java tramite il driver JDBC fornito da M $)


1
Questo potrebbe dipendere dal database. Ad esempio, contare su Postgres è piuttosto lento.
Mike Christensen,

Siamo spiacenti, Java parla a M $ SQL tramite il driver jdbc. Aggiornerò il mio PO.
SnakeDoc,


@Nikola Markovinović: come lo useresti in questo caso?
zerkms,

5
@zerkms Dipende dal contesto. Se in stored procedure sarebbe if exists(select null from products where id = @id); se in una query chiamata direttamente da un client select case when exists (...) then 1 else 0 end.
Nikola Markovinović,

Risposte:


170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; supererà tutti i tuoi suggerimenti in quanto terminerà l'esecuzione dopo aver trovato il primo record.


5
L'ottimizzatore non tiene conto di se stesso durante le ricerche tramite PK (o qualsiasi altra chiave univoca)?
zerkms

3
Non ha mai dichiarato che era il PK, ma in tal caso sì, l'ottimizzatore ne avrebbe tenuto conto.
Declan_K

3
@Declan_K: sembra che la mia sfera magica sia fallita in questo caso e una colonna intitolata come idnon è PK. Quindi +1 al tuo consiglio.
zerkms

4
Se non è il PK, suggerirei anche di assicurarmi che ci sia un indice su quella colonna. Altrimenti, la query dovrà eseguire una scansione della tabella anziché una ricerca della tabella più veloce.
CD Jorgensen,

4
Penso che dovremmo considerare la risposta di @ nenad-zivkovic su questo.
Giulio Caccin,

193

EXISTS(o NOT EXISTS) è appositamente progettato per verificare se esiste qualcosa e pertanto dovrebbe essere (ed è) l'opzione migliore. Si interromperà sulla prima riga corrispondente, quindi non richiede una TOPclausola e in realtà non seleziona alcun dato, pertanto non vi è un sovraccarico nella dimensione delle colonne. Puoi tranquillamente usare SELECT *qui - non diverso da SELECT 1, SELECT NULLo SELECT AnyColumn... (puoi anche usare un'espressione non valida come SELECT 1/0e non si romperà) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END

questo non deve prima eseguire l'istruzione SELECT, quindi eseguire l'istruzione IF EXISTS ... causando un sovraccarico aggiuntivo e quindi più tempo di elaborazione?
SnakeDoc

7
@SnakeDoc No. Existsfunziona selectin modo tale da uscire non appena viene trovata una riga. Inoltre esiste semplicemente una nota dell'esistenza del record, non dei valori effettivi nel record, salvando la necessità di caricare la riga dal disco (presupponendo che i criteri di ricerca siano indicizzati, ovviamente). Per quanto riguarda il sovraccarico di if- dovrai trascorrere comunque questo minuscolo tempo.
Nikola Markovinović,

1
@ NikolaMarkovinović punto interessante. Non sono sicuro che esista un indice in questo campo e il mio SQL newbish non sa come scoprirlo. Sto lavorando con questo DB da Java tramite JDBC e il database si trova in remoto in un colo da qualche parte. Mi è stato fornito solo un "riepilogo del database" che specifica in dettaglio quali campi esistono in ogni tabella, il loro tipo e qualsiasi FK o PK. Questo cambia qualcosa?
SnakeDoc

3
@SnakeDoc Per scoprire la struttura della tabella, inclusi gli indici e le chiavi esterne, eseguire sp_help nome_tabella . Gli indici sono essenziali quando si tratta di recuperare alcune righe da molte, quando si utilizza select topo exists; se non sono presenti il ​​motore sql dovrà eseguire la scansione della tabella. Questa è l'opzione di ricerca della tabella meno desiderabile. Se non sei autorizzato a creare indici, dovrai comunicare con il personale tecnico dall'altra parte per scoprire se li adeguano automaticamente o si aspettano che tu suggerisca gli indici.
Nikola Markovinović,

1
@Konstantin Puoi fare qualcosa del genereSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic,

21

Niente può battere -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Non è necessario contare per sapere se ci sono dati nella tabella. E non usare l'alias quando non è necessario.


5
Nonostante il suo nome idnon è la chiave primaria. Quindi, anche se non stai contando, devi ancora trovare tutti i record corrispondenti, possibilmente migliaia di essi. Informazioni sull'aliasing: il codice è in costante evoluzione. Non sai mai quando dovrai tornare indietro. L'aliasing aiuta a prevenire stupidi errori di runtime; ad esempio, il nome di colonna univoco che non necessitava di un alias non è più univoco perché qualcuno ha creato una colonna con lo stesso nome in un'altra tabella unita.
Nikola Markovinović,

Sì, hai assolutamente ragione. L'aliasing aiuta molto, ma non penso che faccia alcuna differenza quando non si usano i join. Quindi, ho detto di non usarlo se non necessario. :) E puoi trovare una lunga discussione qui sul controllo dell'esistenza. :)
AgentSQL

3
Non so perché ho accettato il termine aliasing. Il termine corretto è qualifying. Ecco una spiegazione più lunga di Alex Kuznetzov . Informazioni sulle query a tabella singola: ora è una tabella singola . Ma più tardi, quando viene scoperto un bug e stai cercando di trattenere il diluvio, il client è nervoso, ti unisci a un altro tavolo solo per affrontare il messaggio di errore - messaggio facilmente correggibile, ma non in questo momento sudato, colpisce un piccolo tratto - e correggi il errore nel ricordare di non lasciare mai una colonna ...
Nikola Markovinović,

1
Non posso ignorarlo ora. Grazie!! :)
AgentSQL

15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

Questo approccio restituisce un valore booleano per te.


1
Probabilmente puoi omettere l'istruzione Top e l'istruzione * per renderla un po 'più veloce, poiché Exist uscirà una volta trovato un record, quindi qualcosa del genere: SELEZIONA CASO QUANDO ESISTE (SELEZIONA 1 DA dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) END
Stefan Zvonar

Questo suggerimento non menziona il motivo per cui questo sarebbe più veloce rispetto alle istruzioni esistenti esistenti / non esistenti in SQL Server. Senza alcun benchmark sarei difficile da credere che un'affermazione del caso avrebbe prodotto un risultato più veloce di una risposta vera / falsa immediata.
Bonez024

8

Puoi anche usare

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END

7

Non pensare che qualcuno l'abbia ancora menzionato, ma se sei sicuro che i dati non cambieranno sotto di te, potresti voler applicare anche il suggerimento NoLock per assicurarti che non venga bloccato durante la lettura.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END

3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Questa è la soluzione di database inter-relazionale che funziona in tutti i database.


7
Tuttavia si forza il db a ciclo su tutti i record, molto lento su grandi tavoli
amd

@amd cura di spiegare perché?
UmNyobe,

@amd il tuo commento ha perfettamente senso. Questa query è più un TROVA TUTTO che TROVA QUALUNQUE.
UmNyobe,

1

Di seguito è riportato il modo più semplice e veloce per determinare se esiste un record nel database o no. La cosa buona è che funziona in tutti i DB relazionali

SELECT distinct 1 products.id FROM products WHERE products.id = ?;

0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;

2
Forse il tuo codice funziona alla grande, ma sarebbe meglio se aggiungi alcune informazioni aggiuntive in modo che siano meglio comprensibili.
idmean

0

L'ho usato in passato e non richiede una scansione completa della tabella per vedere se esiste qualcosa. È super veloce ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             

0

Per coloro che si imbattono in questo da MySQL o Oracle background - MySQL supporta la clausola LIMIT per selezionare un numero limitato di record, mentre Oracle utilizza ROWNUM.

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.