2012-04-10 10 views
5

Tempo interrogativo stupido. Oracle 10g.Dove clausola che riguarda join

È possibile che una clausola where influenzi un join?

Ho una domanda in forma di:

select * from 
(select product, product_name from products p 
join product_serial ps on product.id = ps.id 
join product_data pd on pd.product_value = to_number(p.product_value)) product_result 
where product_name like '%prototype%'; 

Ovviamente questo è un esempio inventato. Nessun reale bisogno di mostrare la struttura del tavolo perché è tutto immaginario. Sfortunatamente, non posso mostrare la struttura o la query reale della tabella. In questo caso, p.product_value è un campo VARCHAR2 che in alcune righe ha un ID memorizzato al suo interno anziché il testo. (Sì, design errato, ma qualcosa che ho ereditato e non riesco a modificare)

Il problema è nell'unione. Se ometto la clausola where, la query funziona e vengono restituite le righe. Tuttavia, se aggiungo la clausola where, ottengo l'errore "numero non valido" sulla condizione di join pd.product_value = to_number (p.product_value).

Ovviamente, l'errore "numero non valido" si verifica quando le righe vengono unite che contengono non cifre nel campo p.product_value. Tuttavia, la mia domanda è come sono selezionate quelle file? Se il join ha esito positivo senza la clausola outer where, la clausola outer where non deve selezionare solo le righe dal risultato del join? Appare ciò che sta accadendo è la clausola where sta influenzando quali righe sono unite, nonostante il join sia in una query interna.

La mia domanda ha senso?

+0

Si potrebbe dare un po 'di spazio 'to_char (pd.product_value) = p.product_value'. – briantyler

risposta

1

Risposta breve: sì.

Risposta lunga: il motore di query è libero di riscrivere la query come preferisce, purché restituisca gli stessi risultati. Tutta la query è disponibile per l'utilizzo al fine di produrre la query più efficiente possibile.

In questo caso, direi che esiste un indice che copre ciò che si desidera, ma non copre il nome del prodotto, quando lo si aggiunge alla clausola where, l'indice non viene utilizzato e c'è una scansione in cui entrambe le condizioni sono testate allo stesso tempo, quindi il tuo errore.

Quale è veramente un errore nella condizione di join, non si dovrebbe usare to_number a meno che non si sia certi che si tratti di un numero.

0

Immagino che il tuo to_number(p.product_value) si applica solo per le righe con un product_name valido.

Quello che succede è che il tuo join viene applicato prima che la tua clausola where determini l'errore della funzione to_number.

Quello che dovete fare è includere il tuo product_name like '%prototype%' come JOIN clausola di come questo:

select * from 
(select product, product_name from products p 
join product_serial ps on product.id = ps.id 
join product_data pd on product_name like '%prototype%' AND 
    pd.product_value = to_number(p.product_value)); 
+0

Questo può o non può aiutare - non c'è nulla che impedisca a Oracle di tentare di valutare prima il 'to_number'. –

2

Colpisce il piano che ha generato.

L'ordine effettivo in cui le tabelle vengono unite (e quindi filtrate) non è dettato dall'ordine in cui si scrive la query, ma dalle statistiche sulle tabelle.

In una versione, il piano generato in coincidenza significa che le righe "cattive" non vengono mai elaborate; poiché i join precedenti hanno filtrato il risultato impostato su un punto a cui non sono mai connessi.

L'introduzione della clausola WHERE ha significato che ORACLE crede ora che un diverso ordine di join sia migliore (perché il filtraggio in base al nome del prodotto richiede un determinato indice o perché limita molto i dati, ecc.).

Questo nuovo ordine indica che le righe "cattive" vengono elaborate prima del join che le filtra.


avrei adopera per pulire i dati prima interrogazione di esso. Probabilmente creando una colonna derivata in cui il valore è già stato convertito in un numero o lasciato come NULL se non è possibile farlo.

È inoltre possibile utilizzare EXPLAIN PLAN per visualizzare i diversi piani gerarchizzati dalle query.

0

Per ulteriori informazioni (e una lettura davvero buona), suggerirei di leggere Subquery Madness di Jonathan Gennick.

Fondamentalmente, il problema è che Oracle è libero di valutare i predicati in qualsiasi ordine. Quindi è libero di spingere (o non premere) il predicato product_name nella sottoquery. È libero di valutare le condizioni di join in qualsiasi ordine. Quindi, se Oracle dovesse scegliere un piano di query in cui filtra le righe non numeriche product_value prima di applicare lo to_number, la query avrà esito positivo. Se capita di scegliere un piano in cui applica lo to_number prima di filtrare le righe non numeriche product_value, si verificherà un errore. Naturalmente, è anche possibile che restituisca con successo le prime N righe e poi riceverai un errore quando proverai a recuperare la riga N + 1 perché la riga N + 1 è la prima volta che tenta di applicare il predicato to_number a un dato non numerico.

Oltre a correggere il modello di dati, è possibile inserire alcuni suggerimenti nella query per forzare Oracle a valutare il predicato che garantisce che tutti i dati non numerici vengano filtrati prima dell'applicazione del predicato to_number. Ma in generale, è un po 'difficile accennare completamente a una query in un modo che costringerà l'ottimizzatore a valutare sempre le cose nell'ordine "corretto".