2009-07-07 7 views
5

Ho letto alcune delle discussioni in this question e ho pensato a me stesso che nel mio codice PL/SQL ho "esistenti" query di stile in tutto il luogo che non usa l'ottimizzazione ROWNUM = 1.In quali condizioni ROWNUM = 1 aumenta significativamente le prestazioni in una query sile "esistente"

Le domande che ho sono:

  1. Fa l'introduzione di ROWNUM = 1 aumentare in modo significativo le prestazioni?
  2. In caso affermativo, in quali condizioni sarebbero le prestazioni siano migliorate in particolare (ad esempio, un sacco di join, vincoli sulle colonne non indicizzati, tabelle di grandi dimensioni, grandi insiemi di risultati)

Sto cercando di determinare sia il valore di riscrivere tutto delle mie query esistenti per aggiungere un'ottimizzazione ROWNUM = 1.

Le query a cui sto pensando sono quelle che possono avere più join e possono eseguire query su tabelle di grandi dimensioni. Hanno la forma generale di:

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions>; 

IF ln_count > 0 THEN 
    <do stuff> 
END IF; 

sto considerando cambiarle a:

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions> 
AND ROWNUM = 1; 

IF <local variable> > 0 THEN 
    <do stuff> 
END IF; 
+0

La tua prima query significa avere SELECT COUNT (1) anziché SELECT 1? –

+0

SELEZIONA 1 (vale a dire selezionare un valore costante per qualsiasi riga restituita invece di contare il numero di righe) – darreljnz

risposta

7

Migliora significativamente le prestazioni (media delle decine di percento) su query che non possono essere risolte mediante una semplice ricerca per indice singolo, ad es. tavolo unisce. Tuttavia è potenzialmente in grado di nascondere l'errore di dati/applicazione.

Consente di avere una tabella:

create table t (id number(10,0), padding varchar2(1000)); 

--intentionally non utilizzare PK per fare l'esempio più semplice possibile. L'imbottitura è utilizzato per simulare il carico reale i dati in ogni record

con molti record:

insert into t (id, padding) 
select rownum, rpad(' ', 1000) from dual connect by level < 10000 

Ora, se si chiede qualcosa di simile

select 1 into ll_exists 
from t where id = 5; 

il DB deve passare attraverso l'intera tabella se ha trovato l'unico record corrispondente nel primo blocco di dati (che tra l'altro non possiamo sapere perché potrebbe essere inserito in molti modi diversi) o nell'ultimo. Questo perché non sa che esiste un solo record corrispondente. D'altra parte se si usa ... e rownum = 1 allora si può smettere di attraversare i dati dopo che il record è stato trovato perché gli è stato detto che non c'è (o non è necessario) un altro record corrispondente.

Lo svantaggio è che con il vincolo di rownum è possibile ottenere risultati non deterministici se i dati contengono più di un record possibile. Se la query è stata

select id into ll_id 
from t where mod (id, 2) = 1 
and rownum = 1; 

poi possono ricevere dalla risposta DB 1 e 3, nonché 123 ... fine non è garantito e questo è la conseguenza. (senza la clausola rownum vorrei ottenere un'eccezione TOO_MANY_ROWS. Dipende dalla situazione quale è peggiore)

Se si desidera veramente eseguire query sulla verifica dell'esistenza, WRITE IT THAT WAY.

begin 

select 'It does' 
    into ls_exists 
from dual where 
exists (your_original_query_without_rownum); 

do_something_when_it_does_exist 
exception 
    when no_data_found then 
    do_something_when_it_doesn't_exist 
end; 
+0

+1, buona risposta dettagliata + note riguardanti la natura non deterministica dei predicati di rownum. –

+0

Alla fine mi piace la conclusione - questo è un modo più appropriato di scriverlo. – darreljnz

+0

La query esterna può essere select count (*) da dual ...(sarà wither 1 o 0), quindi non sarà necessario utilizzare il blocco delle eccezioni per fare la logica del programma (che è male). –

2

ho intenzione di indovinare che questo non sta per essere degno il vostro tempo. I moderni ottimizzatori sono molto bravi in ​​quello che fanno quindi troverei un po 'sorprendente che una query che è CONSENTITA di restituire solo una riga vedrà un significativo incremento delle prestazioni dall'aggiunta di ROWNUM = 1.

Si suppone che il guadagno di prestazioni rimuova la necessità di verificare questo vincolo?

trovo quando smetto di fidarsi l'ottimizzatore spesso scavare me stesso una tomba più profondo;)

Inoltre: In caso di dubbio provarlo. Trova un join di grandi dimensioni, eseguilo più volte senza rownum = 1, più volte con rownum = 1 e verifica se stai notando un notevole miglioramento percentuale. Per garantire che non ci siano problemi di memorizzazione nella cache, suggerirei di farlo su un database che è possibile riavviare.

+1

Passiamo tutti i nostri primi 2 o 3 anni con l'apprendimento SQL non ci sono nuove idee intelligenti. – dkretz

+0

Le query sono "consentite" per restituire una riga, ma qualsiasi cosa dopo la prima viene ignorata poiché mi interessa solo se ci sono risultati. Un esempio pratico dal mio codice. Ho una grande tabella contenente gli ordini dei clienti. Ogni ordine potrebbe avere uno stato (ad es. "In sospeso", "Aperto", "Chiuso", "Annullato") e un identificativo del prodotto. Ho una query per scoprire che ci sono degli ordini aperti per un determinato identificatore del prodotto. So che, in teoria, c'è un guadagno in termini di prestazioni - ciò che mi interessa è se ci sia un significativo aumento delle prestazioni nella pratica. – darreljnz

+0

Non sono d'accordo. C'è una differenza tra una query che è "permesso di restituire una riga" (ad esempio una che calcola un aggregato) e una che esamina solo una riga (ad esempio una con ROWNUM = 1). Confronta: SELECT COUNT (*) FROM my_big_table; vs. SELECT COUNT (*) FROM my_big_table WHERE ROWNUM = 1; - il risultato (in termini di prestazioni) sarà molto diverso. –

5

Una regola empirica nell'ottimizzazione consiste nel non farlo a meno che non si disponga di un hotspot da correggere. Tuttavia, se si è interessati ai vantaggi in termini di prestazioni, è possibile eseguire alcuni test utilizzando entrambi per verificare se è possibile misurare qualsiasi prestazione migliorata.

wikipedia quotes Donald Knuth come dicendo:

"dobbiamo dimenticare piccole efficienze, dicono circa il 97% del tempo: l'ottimizzazione prematura è la radice di tutti i mali".

+0

+1, buon consiglio e bella citazione – DCookie

0

Se si richiede COUNT (1), Oracle deve trovare tutte le righe corrispondenti per soddisfare la risposta esatta.

SELECT COUNT(1) FROM .... 

Se chiedete 1 dalla prima fila, Oracle può fermare una volta che ha trovato una riga corrispondente.

SELECT 1 FROM ... WHERE ROWNUM = 1 

È buona norma chiedere solo i dati effettivamente necessari. Perché ottenere Oracle per dirti che ci sono 1.203.499 risultati corrispondenti quando ti interessa solo il primo?Le persone hanno detto che l'ottimizzatore può migliorare le cose. Tuttavia, deve ancora rispondere alla domanda che hai chiesto. Se fai una domanda più facile, può trovare la risposta più velocemente.

I tempi è probabile avere un impatto significativo sulle prestazioni: - * Il conteggio effettivo di record a trovare è alta, * Oracle passa da un hash JOIN piano per un annidati LOOP, e il piano di ciclo nidificato è meglio per trovare la prima riga

+0

COUNT (1) non significa che otterrà solo una riga. SELECT COUNT (*) FROM my_big_table; è esattamente uguale a SELECT COUNT (1) FROM my_big_table ;. –

+0

Non stavo facendo un punto su COUNT (*) vs COUNT (1). Se chiedi COUNT (1), allora deve contarli tutti. Se chiedi 1 per la prima riga corrispondente, deve solo trovarne uno. –

+0

Abbastanza giusto. Personalmente evito di usare "COUNT (1)" per evitare confusione per i neofiti. –

0

L'introduzione di ROWNUM = 1 aumenta significativamente le prestazioni?

Può fare una differenza molto significativa. Se ti interessa solo la prima riga che il database trova quando esegue la query, allora è meglio dire a Oracle che, aggiungendo "ROWNUM = 1". Se non lo fai, Oracle assumerà che intendi recuperare tutte le righe dalla query alla fine e ottimizzerà la query di conseguenza.

Nel caso di COUNT(), se si desidera solo sapere se è presente almeno un record, il Query Optimiser non lo saprà e conterrà ogni riga, una perdita di tempo. Se aggiungi ROWNUM = 1, hai dato all'ottimizzatore la possibilità di fermarsi non appena trova una riga.

caso affermativo, in quali condizioni sarebbero prestazioni siano particolarmente migliorate (es sacco di join, vincoli sulle colonne non indicizzati, grandi tavoli, grandi insiemi risultato)

I dati più query deve per rispondere alla tua domanda, più il predicato ROWNUM = 1 è in grado di migliorare le prestazioni. In un join a più tabelle, ad esempio, l'aggiunta di ROWNUM = 1 potrebbe modificare il piano dall'utilizzo di molti costosi join di hash, per utilizzare invece cicli nidificati, che sono molto più veloci quando esistono le righe.

+0

Bottom line: l'aggiunta di ROWNUM = 1 NON è solo una misura di "ottimizzazione": dovrebbe essere considerata una parte importante della scrittura di ogni query per fare esattamente ciò che dovrebbe fare, e non di più. –

+0

Wow, il mio primo downvote! :) –

+0

qualcuno si preoccupa di approfondire cosa c'è che non va qui? solo per la mia edificazione ... –

1

Mentre mi piace il più alto votato risposta, nel tentativo di evitare eccezioni, faccio qualcosa di simile:

begin 

    select count(*) 
    into ls_exists 
    from dual 
    where exists (select null from ... where ...); 

    if ls_exists = 1 then 
    do_something; 
    else 
    do_something_else; 
    end if; 

end; 

Ciò sempre restituire una singola riga con un 1 o uno 0 . Nessuna eccezione.

Nota anche l'uso di SELECT NULL. Questo fa un paio di cose; quando l'ottimizzatore pensa che sia appropriato, Oracle controllerà solo gli indici. Se inserisci le colonne della tabella nella clausola SELECT e le colonne non fanno parte di un indice, Oracle eseguirà la ricerca dell'indice, quindi recupererà la riga dalla tabella, che potrebbe essere totalmente inutile in base alla query. Dal momento che stai controllando solo l'esistenza di una riga, probabilmente non hai bisogno di dati reali.

SELECT NULL non restituisce alcun dato (un'ottimizzazione) e la clausola EXISTS controlla se viene restituita una riga, non i dati nella riga.

Problemi correlati