2013-10-07 12 views
8

Ho una tabella con una chiave primaria a più colonne (città/stato/data) e molte altre colonne di dati. Sto cercando di ottenere gli ultimi dati per ogni città/stato. Come lo faccio in modo pulito/efficiente? In questo momento io posso fare questo facendo una prima query per ottenere la lista di tutti i file che sto cercando di recuperare, seguita da una seconda query con una massiccia clausola WHERE:Come selezionare più righe per chiave primaria multi-colonna in MySQL?

SELECT state, city, max(date) from data GROUP BY city, state; 

+-------+---------------------+------------+ 
| state | city    | MAX(date) | 
+-------+---------------------+------------+ 
| CA | San Francisco  | 2013-09-01 | 
| CA | Los Angeles   | 2013-08-01 | 
| NY | New York   | 2013-10-01 | 
| ... | ... (many rows) ... | ...  | 
+-------+---------------------+------------+ 


SELECT * FROM data WHERE 
    (state = "CA" AND city = "San Francisco" AND date='2013-09-01') OR 
    (state = "CA" AND city = "Los Angeles" AND date='2013-08-01') OR 
    (state = "NY" AND city = "New York" AND date='2013-10-01') OR 
    ... 

Questo è veramente brutto e inefficiente e se la prima query restituisce molte righe, la seconda query potrebbe essere troppo lunga. Chiaramente se ho una chiave primaria a colonna singola, potrei usare una sottoselezione con IN(), ma qui non è possibile. Eventuali suggerimenti?

AGGIORNAMENTO: Ho provato il suggerimento di Bill con una sottoselezione, ma non sta usando nessuna chiave e sta prendendo per sempre. Se limito la sottoselezione a restituire solo 5 righe, restituisce in 0,64 s. Se lascio che restituisca tutte le 73 combinazioni città/stato, ci vuole molto tempo (query ancora in esecuzione).

EXPLAIN SELECT * FROM data WHERE (city, state, date) IN (SELECT state, city, MAX(date) FROM data GROUP BY city, state) 
+----+--------------------+-------+-------+---------------+---------+---------+------+-------+-------------+ 
| id | select_type  | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+--------------------+-------+-------+---------------+---------+---------+------+-------+-------------+ 
| 1 | PRIMARY   | data | ALL | NULL   | NULL | NULL | NULL | 13342 | Using where | 
| 2 | DEPENDENT SUBQUERY | data | index | NULL   | PRIMARY | 57  | NULL | 8058 | Using index | 
+----+--------------------+-------+-------+---------------+---------+---------+------+-------+-------------+ 
+1

qual è il risultato finale che si sta tentando di ottenere – Ibu

risposta

4

Penso che questo dovrebbe fare il trucco per voi:

select 
    * 
from 
    data t1 
natural join 
    ( 
     select 
      city, 
      state, 
      max(date) as date 
     from 
      data 
     group by 
      city, 
      state 
    ) t2; 
+0

Sì, funziona perfettamente! Non ho mai usato un join naturale prima, ma è esattamente quello che voglio, ed è veloce! – Jonathan

+0

@ Jonathan - Sono contento di poterti aiutare. Puoi effettivamente usare inner join qui, ma poi devi scrivere la clausola 'on' anche su tutte e 3 le colonne per ottenere lo stesso effetto del join naturale. È molto più chiaro in questo modo e potrebbe essere un po 'più veloce in quanto per il join naturale ci sarà solo una copia per le colonne comuni, ma inner join ne genererà 2. –

4

MySQL supporta i confronti parametri:

SELECT * FROM data WHERE 
(state, city, date) IN (
    ('CA', 'San Francisco', '2013-09-01'), 
    ('CA', 'Los Angeles', '2013-08-01'), 
    ('NY', 'New York', '2013-10-01')); 
+0

Penso che sia stato cercare un approccio dinamico se ci sono - diciamo - qualche hundret o qualche migliaio di città nel tavolo. –

+0

@Bill: non sapevo che MySQL supporta confronti tuple, che sicuramente farà parte della risposta, ma GottliebNotschnabel è corretto, voglio una soluzione dinamica. Sto provando a farlo con una sottoselezione, ma non sta usando la chiave giusta. – Jonathan

+0

L'ho provato prima, sembra che effettui la scansione completa della tabella anche se i criteri corrispondono all'indice. – JasonMing

Problemi correlati