2013-01-05 15 views
6

Sto utilizzando Oracle 11g, la tabella principale ha circa 10 milioni di record. Qui è la mia domanda:Un problema di prestazioni oracle su COUNT()

SELECT COUNT (*) 
    FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS 
WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1; 

Il costo è di 3736, ma quando l'ho cambiato in questa forma:

SELECT COUNT (*) FROM 
    (SELECT 1 FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS 
    WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1); 

Il costo è diventato 5! Qual è la differenza tra queste 2 domande?

Ecco il piano di spiegare sia per query:

La prima query:

---------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |       |  1 | 10 | 3736 (1)| 00:00:45 | 
| 1 | SORT AGGREGATE    |       |  1 | 10 |   |   | 
|* 2 | COUNT STOPKEY    |       |  |  |   |   | 
| 3 | NESTED LOOPS    |       | 4627 | 46270 | 3736 (1)| 00:00:45 | 
| 4 |  TABLE ACCESS BY INDEX ROWID| CONTACT     | 6610 | 33050 | 3736 (1)| 00:00:45 | 
|* 5 |  INDEX RANGE SCAN   | IX_CONTACT_USR   | 6610 |  | 20 (0)| 00:00:01 | 
|* 6 |  INDEX RANGE SCAN   | IX_CONTACT_STATUS  |  1 |  5 |  0 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter(ROWNUM=1) 
    5 - access("C"."USER"=1) 
    6 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1) 

La seconda query:

----------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |       |  1 |  |  5 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE     |       |  1 |  |   |   | 
| 2 | VIEW       |       |  1 |  |  5 (0)| 00:00:01 | 
|* 3 | COUNT STOPKEY    |       |  |  |   |   | 
| 4 |  NESTED LOOPS    |       |  2 | 20 |  5 (0)| 00:00:01 | 
| 5 |  TABLE ACCESS BY INDEX ROWID| CONTACT     |  3 | 15 |  5 (0)| 00:00:01 | 
|* 6 |  INDEX RANGE SCAN   | IX_CONTACT_USR   | 6610 |  |  3 (0)| 00:00:01 | 
|* 7 |  INDEX RANGE SCAN   | IX_CONTACT_STATUS  |  1 |  5 |  0 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - filter(ROWNUM=1) 
    6 - access("C"."USER"=1) 
    7 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1) 

ho eseguito 2 domande, la prima a volte 45s costo + (ad esempio, prima eseguire o modificare l'id utente), altrimenti costerà < 1s. Non so perché è così diverso, forse la cache di db?

Quando ho eseguito la seconda query, posso sempre ottenere risultato in 1 s. Quindi penso che il secondo sia migliore, ma non è il motivo per cui migliora di molto.

+0

Commento sul piano di spiegazioni fornite: Come supponevo, i tuoi due querries non saranno diversi. L'unica differenza è la linea 2 - e ciò non influirà sulle prestazioni ... (notare anche che spiegare piano non è sempre quello che si otterrà in runtime - ecco perché ho proposto di rintracciarlo. – igr

+0

Bella domanda e sicuramente otterrà risposte diverse !Sono contento che tu abbia inserito i piani non molti fanno ed è frustrante, più 1 solo per quello! – glh

+0

puoi pubblicare il piano di spiegazioni per entrambe le query che ci aiuterà a comprendere meglio le statistiche della tabella e confrontare le due query. Per ottenere spieghi il piano di query, segui questo link: http://www.oraclebin.com/2012/12/explain-plan-how-to-get-execution-plan.html –

risposta

1

È possibile vedere dove viene la differenza confrontando la linea nei piani di esecuzione che accedono alla tabella CONTACT (guarda la colonna delle righe, la prima).

Primo:

| 4 |  TABLE ACCESS BY INDEX ROWID| CONTACT     | 6610 | 33050 | 3736 (1)| 00:00:45 | 

Secondo:

| 5 |  TABLE ACCESS BY INDEX ROWID| CONTACT     |  3 | 15 |  5 (0)| 00:00:01 | 

Nel primo esempio, il ROWNUM = 1 predicato non viene applicato fino a dopo il tavolo CONTACT è stata letta, in modo che stai ricevendo 6610 righe restituito da questo tavolo. Mentre nel tuo secondo query optimizer è stato restituito solo 3. Si tratta di molti ordini di grandezza in meno, motivo per cui vedi la seconda query completare più rapidamente.

Per quanto riguarda il motivo per cui la seconda esecuzione della query "lenta" è "veloce", si sta pensando sia corretto - i dati sono stati caricati dal disco nella cache del buffer in modo che l'accesso sia molto più veloce.

+0

Credo che mostri solo come cambia la stima dell'ottimizzatore. La sezione Informazioni Predicato implica che 'filter (ROWNUM = 1)' viene applicato dopo entrambe le tabelle, in entrambi i casi. –

+0

Hai ragione, sta applicando il filtro in entrambi i casi e che il piano di spiegazione è solo una stima. Per essere certi dovremmo vedere l'output di una traccia SQL, ma i test empirici del poster sembrano confermare ciò che è mostrato nel piano: che la prima query sta accedendo a più righe dalla tabella 'CONTACT' che è la seconda. –

+2

Ho eseguito alcuni test e i piani di spiegazione corrispondono a quelli mostrati nella domanda, ma i risultati di tkprof mostrano che accede sempre a una sola riga da entrambe le tabelle in entrambe le query. Quindi è probabile che l'effetto sia interamente memorizzato nella cache e non vi sia (effettivamente) alcuna differenza tra le query –

1

Molto probabilmente questa è solo una differenza di stima e avranno le stesse statistiche di esecuzione. Traccia sia + tkprof per ottenere dati reali. Anche se vuoi qualche altro dettaglio dietro la logica dell'ottimizzatore - fai un analisi approfondita con l'evento 10053.

1

Il costo non è solo il fattore per le query, a volte dipende anche dal server, il che significa che è un costo della CPU o Costo I/O, alcune volte il costo varia, a causa della cardinalità delle colonne, condizioni della query. se vuoi vedere il chiarimento delle query, prendi il piano di spiegazione o TKPROOF, in modo che tu possa sapere, sta andando per la scansione completa della tabella o quale indice sta rilevando e il tempo di esecuzione.