it-swarm.it

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

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 molto tempo a causa del fatto che i join si verificano molto prima del dove.

In questo caso sarebbe più saggio utilizzare wherees invece di join o eventualmente where column in(...)?

20
Jeremy Boyd

Per i moderni RDBMS non c'è 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 è logico elaborazione non 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.

20
gbn

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 una taglia 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.

6
RolandoMySQLDBA

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 penserai per prima, che prevede 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 "id" logico 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 indice poiché TABLE SCAN ha sempre funzionato al meglio.

1
Timothy Oleary