2013-09-25 13 views
5

Ho 2 tabelle che possono essere semplificati a questa struttura:mysql efficiente unire 2 tabelle agli stessi 2 tabelle

Tabella 1:

+----+----------+---------------------+-------+ 
| id | descr_id |  date   | value | 
+----+----------+---------------------+-------+ 
| 1 |  1 | 2013-09-20 16:39:06 |  1 | 
+----+----------+---------------------+-------+ 
| 2 |  2 | 2013-09-20 16:44:06 |  1 | 
+----+----------+---------------------+-------+ 
| 3 |  3 | 2013-09-20 16:49:06 |  5 | 
+----+----------+---------------------+-------+ 
| 4 |  4 | 2013-09-20 16:44:06 | 894 | 
+----+----------+---------------------+-------+ 

Tabella 2:

+----------+-------------+ 
| descr_id | description | 
+----------+-------------+ 
|  1 | abc   | 
+----------+-------------+ 
|  2 | abc   | 
+----------+-------------+ 
|  3 | abc   | 
+----------+-------------+ 
|  4 | DEF   | 
+----------+-------------+ 

Voglio unire la descrizione a table1, filtrare per descrizione in modo da ottenere solo le righe where description = abc e filtrare le righe "duplicate", dove due righe sono duplicate se hanno lo stesso valore e le loro date sono entro 6 mi i nuti l'uno dell'altro. La mia tabella di output desiderata è sotto, (assumendo che abc sia il filtro di descrizione desiderato).

+----+----------+---------------------+-------+-------------+ 
| id | descr_id |  date   | value | description | 
+----+----------+---------------------+-------+-------------+ 
| 1 |  1 | 2013-09-20 16:39:06 |  1 | abc   | 
+----+----------+---------------------+-------+-------------+ 
| 3 |  3 | 2013-09-20 16:49:06 |  5 | abc   | 
+----+----------+---------------------+-------+-------------+ 

La query mi è venuta è:

select * 
    from (
     select * 
      from table1 
      join table2 using(descr_id) 
     where label='abc' 
     ) t1 
    left join (
     select * 
      from table1 
      join table2 using(descr_id) 
     where label='abc' 
     ) t2 on(t1.date<t2.date and t1.date + interval 6 minute > t2.date) 
where t1.value=t2.value. 

Purtroppo questa query richiede più di un minuto di correre con il mio set di dati, e non ha prodotto alcun risultato (anche se credo che ci dovrebbe essere risultati). Esiste un modo più efficiente per eseguire questa query? C'è un modo per nominare una tabella derivata e farvi riferimento più tardi nella stessa query? Inoltre, perché la mia query non restituisce risultati?

Grazie in anticipo per l'aiuto!

modifica: Vorrei conservare il primo di molti campioni con data e ora di chiusura ravvicinata.

La mia tabella1 ha 6,1 milioni di righe, la mia tabella2 ha 30 K, il che mi fa capire che table2 avrebbe solo una riga per la descrizione "abc". Ciò significa che posso solo eseguire una query per descr_id in anticipo, quindi utilizzare quell'id per evitare di unire a table2 l'intera query, rendendolo molto più efficiente. Tuttavia, se la mia tabella2 fosse stata configurata come descritto sopra (il che sarebbe una cattiva progettazione del database, lo ammetto) qual è un buon modo per eseguire tale query?

+1

Speri di mantenere il primo di diversi campioni con timestamp ravvicinati, o l'ultimo di essi, o la media dei loro timestamp, o cosa? Quale timestamp dovrebbe essere nel set di risultati per rappresentare ogni gruppo di campioni vicini? –

+0

Bella domanda BTW +1 quanti dischi ha i tavoli? –

risposta

1

provare a creare tabelle temporanee e unirsi alle tabelle temporanee:

CREATE TEMPORARY TABLE t1 AS (select * 
      FROM table1 
      JOIN table2 USING(descr_id) 
     WHERE label='abc') 

CREATE TEMPORARY TABLE t2 AS (select * 
      FROM table1 
      JOIN table2 USING(descr_id) 
     WHERE label='abc') 

SELECT * 
FROM t1 
LEFT JOIN t2 on(t1.date<t2.date and t1.date + interval 6 minute > t2.date) 
WHERE t1.value=t2.value 

Le tabelle temporanee vengono puliti automaticamente dopo la disconnessione dal database quindi non c'è bisogno di farli cadere in modo esplicito.

Inizialmente avevo questo, ma non credo che siano raggiunti gli requisiti completi:

SELECT t1.id, 
     t1.descr_id, 
     t1.date, 
     t1.value, 
     t2.description 
FROM table1 t1 
JOIN table2 t2 ON t1.descr_id = t2.descr_id 
WHERE t2.description = 'abc' 

Questo è essenzialmente lo stesso della query originale, tuttavia un'altra opzione potrebbe essere quella di creare una vista e di unirsi a la vista in questo modo:

CREATE VIEW v1 AS 
SELECT * FROM table1 JOIN table2 USING(descr_id) WHERE label='abc' 

CREATE VIEW v2 AS 
SELECT * FROM table1 JOIN table2 USING(descr_id) WHERE label='abc' 

SELECT * 
FROM v1 
LEFT JOIN v2 on(v1.date<v2.date and v1.date + interval 6 minute > v2.date) 
WHERE v1.value=v2.value 

Inoltre, se si esegue questa query su base regolare, si potrebbe considerare di caricare i risultati della tua prima query in una tabella di gestione temporanea e fare la vostra unirsi sulla tabella di gestione temporanea come questa:

INSERT INTO staging 
(SELECT * 
     FROM table1 
     JOIN table2 USING(descr_id) 
     WHERE label='abc') 

SELECT * 
    FROM staging s1 
    LEFT JOIN staging s2 on(s1.date<s2.date and s1.date + interval 6 minute > s2.date) 
    WHERE s1.value=s2.value 

TRUNCATE TABLE staging 
+0

Si prega di non utilizzare questo anti-pattern SQL ... molto cattivo modo di fare questo ... perché questo può causare una tabella basata su disco myisam ... –

+0

Fare cadere i tavoli dopo l'uso è una pratica migliore? O le tabelle temporanee non dovrebbero essere usate affatto? –

+1

tabella temporanea dovrebbe essere evitare che potrebbe causare il disco myisam vedere questo http://dev.mysql.com/doc/refman/5.7/en/internal-temporary-tables.html it max_heap_table_size è troppo piccolo accadrà –

0

tenta di utilizzare non esiste qualcosa come select * da table1 t1 unirsi table2 t2 utilizzando (descr_id) dove label = 'abc' e non esiste (selezionare * da table1 t11 unirsi table2 t22 utilizzando (descr_id) dove label = 'abc' e t1.Data < t11.date e t1.date + intervallo di 6 minuti> t11.date)

potrebbe essere necessario controllare due volte il (t1.date + intervallo di 6 minuti) sintassi