2009-09-04 10 views
27

Sto cercando di recuperare il primo e l'ultimo record di un record "raggruppato".
Più precisamente, sto facendo una query come questaCome recuperare il primo e l'ultimo record di un record raggruppato in una query MySQL con funzioni di aggregazione?

SELECT MIN(low_price), MAX(high_price), open, close 
FROM symbols 
WHERE date BETWEEN(.. ..) 
GROUP BY YEARWEEK(date) 

ma mi piacerebbe ottenere il primo e l'ultimo record del gruppo. Si potrebbe fare facendo tonnellate di richieste ma ho un tavolo abbastanza grande.

C'è un (tempo di elaborazione basso se possibile) per farlo con MySQL?

+0

Per una maggiore efficienza, vedere http://mysql.rjweb.org/doc.php/groupwise_max –

risposta

51

si desidera utilizzare GROUP_CONCAT e SUBSTRING_INDEX:

SUBSTRING_INDEX(GROUP_CONCAT(CAST(open AS CHAR) ORDER BY datetime), ',', 1) AS open 
SUBSTRING_INDEX(GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1) AS close 

Questo evita query sub costosi e lo trovo generalmente più efficiente per questo particolare problema.

Controllare le pagine di manuale per entrambe le funzioni per comprendere i loro argomenti o visitare questo articolo che include un esempio di come fare timeframe conversion in MySQL per ulteriori spiegazioni.

+0

O come salvare una vita. –

+3

Grazie per la soluzione furba!Tuttavia, trovo sfortunato che MySQL non supporti FIRST() e LAST(), che sarebbe molto più veloce di questo ... – Aweb

+0

Soluzione eccellente. Mi sono interrogato sulle prestazioni e le considerazioni sulla memoria su tabelle grandi fino a quando ho visto che l'operazione è limitata alla dimensione definita da 'group_concat_max_len' (default 1024). Bei tempi! –

2

Try This per cominciare ...:

Select YearWeek, Date, Min(Low_Price), Max(High_Price) 
From 
    (Select YEARWEEK(date) YearWeek, Date, LowPrice, High_Price 
    From Symbols S 
    Where Date BETWEEN(.. ..) 
    GROUP BY YEARWEEK(date)) Z 
Group By YearWeek, Date 
+1

Bellezza! Avevo bisogno di min, max per la stessa subquery per cui ho recuperato. Grazie!! – curlyreggie

-1

Supponendo che si desidera gli ID dei record con il LOW_PRICE più basso e il più alto HIGH_PRICE si potrebbe aggiungere queste due colonne per la tua ricerca,

SELECT 

(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id, 
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id, 

MIN(low_price), MAX(high_price), open, close 
FROM symbols 
WHERE date BETWEEN(.. ..) 
GROUP BY YEARWEEK(date) 

Se l'efficienza è un problema, è necessario aggiungere una colonna per "year_week", aggiungere alcuni indici di copertura e dividere la query in due.

La colonna 'year_week' è solo un INT impostato sul valore di YEARWEEK (data) e aggiornato ogni volta che la colonna 'data' viene aggiornata. In questo modo non devi ricalcolarlo per ogni query e puoi indicizzarlo.

I nuovi indici di copertura dovrebbero apparire così. L'ordine è importante. KEY yw_lp_id (year_week, LOW_PRICE, id), KEY yw_hp_id (year_week, HIGH_PRICE, id)

Si dovrebbe quindi utilizzare queste due query

SELECT 
(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id, 
MIN(low_price), open, close 
FROM symbols 
WHERE year_week BETWEEN(.. ..) 
GROUP BY year_week 

e

SELECT 
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id, 
MAX(high_price), open, close 
FROM symbols 
WHERE year_week BETWEEN(.. ..) 
GROUP BY year_week 

copre indici sono abbastanza utile. Controlla this per ulteriori dettagli.

0

Ecco una soluzione specifica per questo problema specifico: http://topwebguy.com/first-and-last-in-mysql-a-working-solution/ È quasi semplice come utilizzare FIRST e LAST in MySQL.

io includere il codice che in realtà fornisce la soluzione ma si può guardare UPI l'intero testo:

SELECT 
word , 

(SELECT a.ip_addr FROM article a 
WHERE a.word = article.word 
ORDER BY a.updated LIMIT 1) AS first_ip, 

(SELECT a.ip_addr FROM article a 
WHERE a.word = article.word 
ORDER BY a.updated DESC LIMIT 1) AS last_ip 

FROM notfound GROUP BY word;