2013-01-05 15 views
6

Qui di seguito è una dichiarazione di SQL all'interno di una stored procedure (troncato per brevità):MySQL DOVE NON estremamente lento

SELECT * 
FROM item a 
WHERE a.orderId NOT IN (SELECT orderId FROM table_excluded_item); 

Questa affermazione richiede 30 secondi o giù di lì! Ma se rimuovo la query SELECT interna, scende a 1s. table_excluded_item non è enorme, ma sospetto che la query interna sia eseguita più di quanto debba essere.

Esiste un modo più efficiente per farlo?

+1

La query interna è una sottoquery dipendente, che è un noto collo di bottiglia perché la sottoquery viene eseguita su ogni riga della query esterna. Controlla [Ottimizzazione sottoquery] (http://dev.mysql.com/doc/refman/5.1/en/optimizing-subqueries.html) sul sito dev MySQL. – Anthony

risposta

14

uso LEFT JOIN

SELECT a.* 
FROM item a 
     LEFT JOIN table_excluded_item b 
      ON a.orderId = b.orderId 
WHERE b.orderId IS NULL 

assicurarsi che orderId da entrambe le tabelle è stato indicizzato.

+0

Ehi, funziona, grazie! Ora è giù a 2 secondi. La lingua mi sembra sempre molto controintuitiva :( – pixelfreak

+0

prego ": D' –

1

Prova questo e confrontare il tempo LEFT JOIN query:

SELECT * 
FROM item a 
HAVING orderId NOT IN (SELECT orderId FROM table_excluded_item); 

Questa è disapprovato (usando HAVING Quando si può utilizzare WHERE) dal HAVING ipotizza che la condizione di limitazione (orderId) è parte del risultato impostato. Ma penso che in questi scenari abbia più senso (dato che fa parte del set di risultati) e perché è più chiaro ciò che sta accadendo rispetto all'approccio LEFT JOIN.

In realtà potrebbe essere un po 'più lento, ma pubblicare i risultati in modo da sapere se è meglio della query originale.

+0

applausi - sul mio set di dati ho ottenuto costantemente ~ 7s per questo vs ~ 8s per l'approccio di join sinistro – hoju

5

Il problema con l'approccio di join sinistro è che i record duplicati potrebbero essere elaborati nel generare l'output. A volte, questo non è il caso. . . in base a questo article, MySQL ottimizza correttamente left outer join quando le colonne vengono indicizzate, anche in presenza di duplicati. Ammetto di rimanere scettico, però, che questa ottimizzazione accade sempre.

MySQL a volte ha problemi nell'ottimizzazione delle affermazioni IN con una sottoquery. La migliore soluzione è una subquery correlata:

SELECT * 
FROM item a 
WHERE not exists (select 1 
        from table_excluded_item tei 
        where tei.orderid = a.orderid 
        limit 1 
       ) 

Se si dispone di un indice su table_excluded_item.orderid, allora questa sarà la scansione l'indice e fermarsi al primo valore (il limit 1 possono non essere strettamente necessario per questo). Questo è il modo più rapido e sicuro per implementare ciò che vuoi in MySQL.

+2

Tecnicamente il' limite 1' è non è necessario, "anti-join" farà esattamente la stessa cosa comunque (potrebbe * essere * che mysql non sia abbastanza intelligente per saperlo) – wildplasser

+0

"Il problema con l'approccio left join è che puoi ottenere record duplicati in l'output. "- perché? Ricordare che si sta cercando un record inesistente. –

+0

@jW. Ho riformulato questo. –

Problemi correlati