2013-02-19 10 views
5

Stavo attraversando SQLZOO "SELECT within SELECT tutorial" ed ecco una delle query che ha fatto il lavoro (task)È una query lenta? Può essere migliorato?

mondo (nome, continente, zona, popolazione, PIL)

SELECT w1.name, w1.continent, w1.population 
FROM world w1 
WHERE 25000000 >= ALL(SELECT w2.population FROM world w2 WHERE w2.continent=w1.continent) 

Le mie domande riguardano l'efficacia di tale query. La sottoquery verrà eseguita per ogni riga (paese) della query principale e quindi ripetutamente popolando nuovamente l'elenco ALL per un determinato continente.

  1. Devo essere preoccupato o l'ottimizzazione Oracle in qualche modo si prenderà cura di esso?
  2. Può essere riprogrammato senza una sottoquery correlata?
+0

Ecco un violino per i dati/query: http://sqlfiddle.com/#!4/2aed1/1 – Wilduck

risposta

1

Se si vuole riscrivere la query senza una sottoquery correalted, qui è un modo:

SELECT w1.name, w1.continent, w1.population 
FROM world w1 
    JOIN 
    (SELECT continent, MAX(population) AS max_population 
     FROM world 
     GROUP BY continent 
    ) c 
    ON c.continent = w1.continent 
WHERE 25000000 >= c.max_population ; 

Non sottintende che questo sarà più veloce. L'ottimizzatore di Oracle è abbastanza buono e questa è una semplice query generale, comunque tu la scriva. Ecco un altro semplificazione:

SELECT w1.name, w1.continent, w1.population 
FROM world w1 
    JOIN 
    (SELECT continent 
     FROM world 
     GROUP BY continent 
     HAVING MAX(population) <= 25000000 
    ) c 
    ON c.continent = w1.continent ; 
+0

Mi piacciono le tue soluzioni (specialmente la seconda). Credo che questo sia quello che stavo cercando. –

3

Prima di tutto è necessario capire come trasformare Oracle questa query per valutare.

SELECT w1.name 
    , w1.continent 
    , w1.population 
FROM world w1 
WHERE 25000000 >= ALL(SELECT w2.population 
         FROM world w2 
         WHERE w2.continent=w1.continent 
        ); 

Ora l'ottimizzatore trasforma una condizione che utilizza l'operatore di confronto ALL seguita da una subquery in una condizione equivalente che utilizza l'QUALSIASI operatore di confronto e un operatore di confronto complementare

SELECT w1.name 
     , w1.continent 
     , w1.population 
    FROM world w1 
    WHERE NOT(25000000 < ANY (SELECT w2.population 
         FROM world w2 
         WHERE w2.continent=w1.continent) 
     ); 

l'ottimizzatore poi ulteriori trasformazioni la seconda query nella seguente query utilizzando la regola per la trasformazione delle condizioni con l'operatore di confronto ANY seguito da una sottoquery correlata:

SELECT w1.name 
     , w1.continent 
     , w1.population 
    FROM world w1 
    WHERE 
    NOT EXISTS (SELECT w2.population 
        FROM world w2 
       WHERE w2.continent=w1.continent 
        AND 25000000 < w2.population 
       ); 

Questo ho preso da fonte Oracle Link

per le vostre domande:

  1. Sì Oracle si prenderà cura di questo, come suggerisce la trasformazione, come Oracle trasformare il sopra query.But capire meglio come questo fine risultato lavoro di query.
  2. sì, questo può essere fatto senza sub-query correlata, ma comunque è necessario unirsi alla stessa tabella perché è necessario confrontare altri record nella tabella che ha lo stesso continente. [Si prega di correggermi se ho torto]
+0

pensavo (ma non ho trovato) una soluzione che costruisce un ' elenco statico di tutti i continenti che corrispondono al criterio valutando ogni continente ** solo una volta **. –

1

è possibile semplificare questo senza la necessità di eseguire la scansione del tavolo due volte:

select a.name, a.continent, a.population, a.max_pop 
    from (select w1.name, w1.continent, w1.population, 
       max(w1.population) over (partition by w1.continent) max_pop 
      from world w1 
     ) a 
where 25000000 >= a.max_pop;