2013-08-25 20 views
5

Ho una tabella di grandi dimensioni con circa 100 milioni di record, con campi start_date e end_date, con tipo DATE. Devo controllare il numero di sovrapposizioni con un intervallo di date, ad esempio tra 2013-08-20 E 2013-08-30, Quindi io uso.ricerca per data prestazione mysql

SELECT COUNT(*) FROM myTable WHERE end_date >= '2013-08-20' 
AND start_date <= '2013-08-30' 

colonna di data sono indicizzati. I punti importanti sono che gli intervalli di date che sto cercando di sovrapporre sono sempre in futuro, mentre la parte principale dei record nella tabella sono nel passato (ad esempio circa 97-99 milioni). Quindi, sarà questa query più veloce, se posso aggiungere una colonna is_future - TINYINT, quindi, controllando solo che condizione come questa

SELECT COUNT(*) FROM myTable WHERE is_future = 1 
AND end_date >= '2013-08-20' AND start_date <= '2013-08-30' 

che esclude il resto 97 milioni o giù di lì dischi e controllerà la condizione data solo i restanti 1-3 milioni di dischi?

Io uso MySQL

Grazie

EDIT

Il motore di MySQL è InnoDB, ma importa molto se si tratta di dire, MyISAM

qui è la tabella di creare

CREATE TABLE `orders` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `title` 
    `start_date` date DEFAULT NULL, 
    `end_date` date DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 

EDIT 2 dopo @ Robert Co rispondere

Il partizionamento si presenta come una buona idea per questo caso, ma non mi permette di creare partizioni in base a is_future campo a meno che io definisco come chiave primaria, altrimenti dovrei rimuovere la mia chiave primaria principale - id, che non posso fare. Quindi, se definisco quel campo come chiave primaria, allora c'è un significato di partizionamento, non sarà veloce già se cerco nel campo is_future che è la chiave primaria.

EDIT 3 La query effettivo in cui ho bisogno di utilizzare questo è quello di selezionare ristorante che hanno alcuni tavoli liberi per questo intervallo di date

SELECT r.id, r.name, r.table_count 
FROM restaurants r 
LEFT JOIN orders o 
ON r.id = o.restaurant_id 
WHERE o.id IS NULL 
OR (r.table_count > (SELECT COUNT(*) 
       FROM orders o2 
       WHERE o2.restaurant_id = r.id AND 
       end_date >= '2013-08-20' AND start_date <= '2013-08-30' 
       AND o2.status = 1 
      ) 
) 

SOLUZIONE Dopo molta più ricerca e sperimentazione del il modo più rapido per contare il numero di righe nel mio caso era quello di aggiungere solo un'altra condizione, che data_inizio è più della data corrente (perché gli intervalli di date per la ricerca sono sempre in futuro)

SELECT COUNT(*) FROM myTable WHERE end_date >= '2013-09-01' 
     AND start_date >= '2013-08-20' AND start_date <= '2013-09-30' 

inoltre è necessario avere un indice - con i campi start_date e end_date (grazie @symcbean). Come risultato, il tempo di esecuzione su una tabella con righe da 10 m da 7 secondi è diventato 0,050 secondi.

SOLUZIONE 2 (@Robert Co) partizionamento in questo caso ha funzionato pure !! - forse è una soluzione migliore dell'indicizzazione. Oppure possono essere entrambi applicati insieme.

Grazie

+0

Ottima domanda ... provalo e facci sapere. ;) Perché non stai usando 'BETWEEN'? – DevlshOne

+1

@DevlshOne,: D, come utilizzare in questo caso? Confronto 2 colonne, posso? – dav

+0

Puoi pubblicare la dichiarazione della tabella di creazione e le informazioni sul motore di archiviazione, questo è importante .. –

risposta

4

Questo è un caso di utilizzo perfetto per table partitioning. Se la funzione di INTERVALLO Oracle lo rende su MySQL, allora aggiungerà semplicemente alla suggestione.

+0

Non sei proprio sicuro del motivo per cui pensi che una tabella 'ordini' la squalifichi dal partizionamento. È possibile creare le partizioni in anticipo, nel caso in cui non si desideri che i dati cadano nel MAXVALUE. –

+0

Ho aggiornato la domanda. Grazie – dav

+1

La chiave di partizionamento non deve essere uguale alla chiave primaria. Se scegli il partizionamento, non hai più bisogno del flag is_future. Vorrei partizionare entro la data di fine. Non esagerare con il partizionamento. Mensile dovrebbe essere sufficiente. –

0

ho fatto un semplice test, appena creato un indice sulla colonna tinyint. Le strutture potrebbero non essere le stesse, ma con un indice sembra funzionare.

http://www.sqlfiddle.com/#!2/514ab/1/0 e per il conteggio http://www.sqlfiddle.com/#!2/514ab/2/0

piano Vista esecuzione lì a vedere che il selezionare solo analizza una riga che significa che avrebbe elaborare solo il minor numero di record nel tuo caso.

Quindi la risposta semplice è sì, con un indice funzionerebbe.

+1

No non indicizzare una colonna con una bassa selettività solo due valori 0 - 1 non dovrebbero mai essere indicizzati .. –

+0

Ok ho imparato qualcosa, ma poi la risposta è NO, perché senza l'indice seleziona tutte e tre le righe, io indovina la risposta è ovvia – skv

+1

@RaymondNijland, a meno che tu non voglia che la colonna sia nell'indice di copertura, altrimenti richiederà (per lo più casuale) il disco io per ogni riga. Mai dire mai. – newtover

2

data colonna vengono indicizzati

Che tipo di indice? Un indice basato su hash non è utile per le query di intervallo. Se non è un indice BTREE, cambialo ora. E non ci hai mostrato * come sono indicizzati. Sono entrambe le colonne nello stesso indice? Ci sono altre cose lì dentro? Quale ordine (end_date deve apparire come prima colonna)?

Ci sono conversioni di tipo implicite nello script - questo dovrebbe essere gestito automaticamente dal ottimizzatore, ma vale la pena controllare ....

SELECT COUNT(*) FROM myTable WHERE end_date >= 20130820000000 
AND start_date <= 20130830235959 

se posso aggiungere una colonna di is_future - TINYINT

Innanzitutto, per essere utile, ciò richiederebbe che le date future siano una piccola percentuale dei dati totali memorizzati nella tabella (meno del 10%). E questo è solo per renderlo più efficiente di una scansione completa della tabella.

In secondo luogo, richiederà un aggiornamento molto frequente dell'indice per mantenerlo, che oltre al sovraccarico del populatiopn iniziale può portare alla frammentazione dell'indice e alle prestazioni degradate (a seconda di come è costruito lo iondex) .

In terzo luogo, se questo deve ancora elaborare 3 milioni di righe di dati (e in particolare, tramite una ricerca dell'indice), sarà molto lento anche con i dati bloccati in memoria.

Inoltre, l'ottimizzatore non utilizzerà mai questo indice senza essere costretto a (a causa della bassa cardinalità).

+0

Ho aggiornato la domanda. Informazioni sull'indice - è un normale indice che viene creato da 'ALTER TABLE ordina ADD INDEX endDate (end_date);', lo stesso per 'start_date', non è giusto? – dav

+0

Questo non risponde alla domanda su che tipo di indice è - ma scoprirai che lasciarli cadere e sostituirli con un singolo indice (end_date, start_date, ...) funzionerà molto meglio. E viste le maggiori informazioni che hai fornito dovrebbe probabilmente essere (end_date, start_date, restaurant_id). La query che stai utilizzando per trovare le tabelle è lontana dall'ottimale – symcbean

+0

ma rispetto a quale è il modo di scoprire che tipo di indice è? senza sostituire l'indice con indice singolo cos'altro posso fare per ottimizzare questa query? o posso trovare i ristoranti con tavoli gratuiti con altre query? Grazie – dav