Cosa è più efficiente, una clausola where o un'unione con oltre milioni di righe?


17

Eseguiamo un sito Web con 250MM di righe in una tabella e in un'altra tabella a cui ci uniamo per la maggior parte delle query ha appena meno di 15MM di righe.

Strutture campione:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Dobbiamo regolarmente fare alcune domande su tutte queste tabelle. Uno sta prendendo le statistiche per gli utenti gratuiti (~ 10k utenti gratuiti).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

Il problema è che questa query verrà eseguita a volte per un tempo dannatamente lungo a causa del fatto che i join avvengono molto prima del dove.

In questo caso, sarebbe più saggio utilizzare Wherees invece di Join o possibilmente where column in(...)?


1
Quale database e versione?
Leigh Riffel,

1
hai provato in entrambi i modi?
gbn,

Se fosse Oracle, creerei un indice basato sulle funzioni per UserTable su NVL2 (Role, NULL, ID), ma questo sembra un altro DB.
Leigh Riffel,

Risposte:


20

Per i moderni RDBMS non esiste alcuna differenza tra "JOIN esplicito" e "JOIN-in-the-WHERE" (se tutti i JOIN sono INTERNI) per quanto riguarda le prestazioni e il piano di query.

La sintassi JOIN esplicita è più chiara e meno ambigua (vedere i collegamenti seguenti)

Ora, JOIN-before-WHERE è un'elaborazione logica non un'elaborazione effettiva e i moderni ottimizzatori sono abbastanza intelligenti da rendersene conto.

Il tuo problema qui è molto probabilmente l'indicizzazione.

Mostraci tutti gli indici e le chiavi su queste tabelle. E i piani di query

Nota: questa domanda sarebbe stata chiusa su StackOverflow per essere ormai un duplicato ... COUNT (1) vs COUNT (*) è un altro mito rotto.


2
NON È SEMPRE VERO che non vi siano differenze tra joine la whereclausola. Ottimizzo continuamente le query a esecuzione prolungata e, talvolta, le query che utilizzano la whereclausola using funzionano meglio di quelle che utilizzano joincon un fattore fino a 70x. Se fosse così semplice e diretto, la vita sarebbe tutta arcobaleni e unicorni. E non si tratta di un antico motore oscuro - in questo momento sto guardando 70x vantaggi della whereclausola in SQL 2012.
aje

Inoltre, osservo spesso gli stessi identici piani da entrambi gli approcci e isolato le query eseguono esattamente lo stesso, ma quando la wherequery della clausola viene eseguita all'interno del batch di grandi dimensioni, dovrebbe essere parte di essa, supera joindi gran lunga la query. Le query SQL non vengono eseguite nel vuoto: sono influenzate dal resto del payload del server e spesso le wherequery della clausola funzionano abbastanza bene, il che è una seccatura poiché la joinsintassi è davvero molto più pulita.
aje

3
@ajeh: vorrei suggerire che la tua esperienza è molto atipica. Hai problemi più grandi con le query se hai differenze x70: è così semplice
gbn

5

Devi riformattare la query del tutto

Prova a eseguire prima le clausole WHERE e successivamente i JOIN

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Anche se esegui un piano EXPLAIN su questa query refactored e sembra peggiore dell'originale, provalo comunque. Le tabelle temporanee create internamente eseguiranno join cartesiani ma quelle tabelle sono più piccole con cui lavorare.

Ho avuto questa idea da questo video di YouTube .

Ho provato i principi del video in una domanda molto complessa in StackOverflow e ho ottenuto un premio di 200 punti.

@gbn menzionato assicurandosi di disporre degli indici giusti. In questo caso, indicizza la colonna creata in MasterTable.

Provaci !!!

AGGIORNAMENTO 2011-06-24 22:31 EDT

È necessario eseguire queste query:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Se NullRoles X 20 <AllRoles (in altre parole, se NullRoles è inferiore al 5% delle righe della tabella), è necessario creare un indice non univoco il ruolo in UserTable. Altrimenti, basterebbe una tabella completa di UserTable poiché lo Strumento per ottimizzare le query potrebbe escludere utilizzando un indice.

AGGIORNAMENTO 2011-06-25 12:40 EDT

Dal momento che sono un DBA MySQL, il mio metodo di fare le cose richiede di non fidarmi di MySQL Query Optimizer attraverso pessimismo positivo ed essere conservatore. Quindi, proverò a refactoring di una query o alla creazione di indici di copertura necessari per andare avanti con le cattive abitudini nascoste di MySQL Query Optimizer. La risposta di @ gbn sembra più completa in quanto SQL Server potrebbe avere più "solidità" nel valutare le query.


0

Avevamo una tabella [Dettagli] di circa 75 milioni di righe; una tabella [Master] di circa 400K righe e una tabella [Item] correlata che aveva 7 righe, sempre e per sempre. Memorizzava la piccola serie di "Numeri oggetto" (1-7) e modellava un modulo cartaceo, milioni dei quali venivano stampati e distribuiti ogni mese. La query più veloce è stata quella a cui probabilmente penseresti per prima, coinvolgendo l'uso di un'unione cartesiana. IIRC, era qualcosa del tipo:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Anche se esiste un collegamento logico "id" tra [Item] e [Detail], CROSS JOIN ha funzionato meglio di INNER JOIN.

RDBMS era Teradata con la sua tecnologia MPP e IDR quale era lo schema di indicizzazione. La tabella a 7 righe non aveva alcun indice poiché TABLE SCAN ha sempre offerto il meglio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.