Un modo semplice per calcolare la mediana con MySQL

Qual è il modo più semplice (e si spera non troppo lento) per calcolare la mediana con MySQL? Ho usato AVG(x) per trovare la media, ma ho difficoltà a trovare un modo semplice per calcolare la mediana. Per ora, sto restituendo tutte le righe a PHP, facendo un ordinamento, e quindi selezionando la riga centrale, ma sicuramente ci deve essere un modo semplice per farlo in una singola query MySQL.

Dati di esempio:

 id | val -------- 1 4 2 7 3 2 4 2 5 9 6 8 7 3 

L’ordinamento su val2 2 3 4 7 8 9 , quindi la mediana dovrebbe essere 4 , rispetto a SELECT AVG(val) che == 5 .

    In MariaDB / MySQL:

     SELECT AVG(dd.val) as median_val FROM ( SELECT d.val, @rownum:=@rownum+1 as `row_number`, @total_rows:=@rownum FROM data d, (SELECT @rownum:=0) r WHERE d.val is NOT NULL -- put some where clause here ORDER BY d.val ) as dd WHERE dd.row_number IN ( FLOOR((@total_rows+1)/2), FLOOR((@total_rows+2)/2) ); 

    Steve Cohen sottolinea che dopo il primo passaggio, @rownum conterrà il numero totale di righe. Questo può essere usato per determinare la mediana, quindi non è necessario alcun secondo passaggio o join.

    Anche AVG(dd.val) e dd.row_number IN(...) vengono utilizzati per produrre correttamente una mediana quando c’è un numero pari di record. Ragionamento:

     SELECT FLOOR((3+1)/2),FLOOR((3+2)/2); -- when total_rows is 3, avg rows 2 and 2 SELECT FLOOR((4+1)/2),FLOOR((4+2)/2); -- when total_rows is 4, avg rows 2 and 3 

    Infine, MariaDB 10.3.3+ contiene una funzione MEDIANTE

    Ho appena trovato un’altra risposta online nei commenti :

    Per le mediane in quasi tutti gli SQL:

     SELECT x.val from data x, data y GROUP BY x.val HAVING SUM(SIGN(1-SIGN(y.val-x.val))) = (COUNT(*)+1)/2 

    Assicurati che le tue colonne siano ben indicizzate e che l’indice sia usato per filtrare e ordinare. Verifica con i piani di spiegazione.

     select count(*) from table --find the number of rows 

    Calcola il numero di riga “mediana”. Forse usare: median_row = floor(count / 2) .

    Quindi prendilo dalla lista:

     select val from table order by val asc limit median_row,1 

    Questo dovrebbe restituirti una riga con solo il valore che desideri.

    Giacobbe

    Ho trovato che la soluzione accettata non funzionava sulla mia installazione MySQL, restituendo un set vuoto, ma questa query ha funzionato per me in tutte le situazioni in cui l’ho testata:

     SELECT x.val from data x, data y GROUP BY x.val HAVING SUM(SIGN(1-SIGN(y.val-x.val)))/COUNT(*) > .5 LIMIT 1 

    Sfortunatamente, né le risposte di TheJacobTaylor né quelle di velcro restituiscono risultati accurati per le versioni correnti di MySQL.

    La risposta del velcro dall’alto è vicina, ma non calcola correttamente per i set di risultati con un numero pari di righe. Le medie sono definite come 1) il numero medio su serie numerate dispari, o 2) la media dei due numeri centrali su serie di numeri pari.

    Quindi, ecco la soluzione velcro adattata per gestire sia i numeri dispari che quelli pari:

     SELECT AVG(middle_values) AS 'median' FROM ( SELECT t1.median_column AS 'middle_values' FROM ( SELECT @row:=@row+1 as `row`, x.median_column FROM median_table AS x, (SELECT @row:=0) AS r WHERE 1 -- put some where clause here ORDER BY x.median_column ) AS t1, ( SELECT COUNT(*) as 'count' FROM median_table x WHERE 1 -- put same where clause here ) AS t2 -- the following condition will return 1 record for odd number sets, or 2 records for even number sets. WHERE t1.row >= t2.count/2 and t1.row < = ((t2.count/2) +1)) AS t3; 

    Per utilizzare questo, segui questi 3 semplici passaggi:

    1. Sostituisci "median_table" (2 ricorrenze) nel codice sopra con il nome della tua tabella
    2. Sostituisci "median_column" (3 occorrenze) con il nome della colonna per il quale desideri trovare una mediana
    3. Se hai una condizione WHERE, sostituisci "WHERE 1" (2 ricorrenze) con la tua condizione

    Propongo un modo più veloce.

    Ottieni il conteggio delle righe:

    SELECT CEIL(COUNT(*)/2) FROM data;

    Quindi prendi il valore medio in una sottoquery ordinata:

    SELECT max(val) FROM (SELECT val FROM data ORDER BY val limit @middlevalue) x;

    Ho provato questo con un set di dati 5x10e6 di numeri casuali e troverà la mediana in meno di 10 secondi.

    Un commento su questa pagina nella documentazione MySQL ha il seguente suggerimento:

     -- (mostly) High Performance scaling MEDIAN function per group -- Median defined in http://en.wikipedia.org/wiki/Median -- -- by Peter Hlavac -- 06.11.2008 -- -- Example Table: DROP table if exists table_median; CREATE TABLE table_median (id INTEGER(11),val INTEGER(11)); COMMIT; INSERT INTO table_median (id, val) VALUES (1, 7), (1, 4), (1, 5), (1, 1), (1, 8), (1, 3), (1, 6), (2, 4), (3, 5), (3, 2), (4, 5), (4, 12), (4, 1), (4, 7); -- Calculating the MEDIAN SELECT @a := 0; SELECT id, AVG(val) AS MEDIAN FROM ( SELECT id, val FROM ( SELECT -- Create an index n for every id @a := (@a + 1) mod oc AS shifted_n, IF(@a mod oc=0, oc, @a) AS n, o.id, o.val, -- the number of elements for every id oc FROM ( SELECT t_o.id, val, c FROM table_median t_o INNER JOIN (SELECT id, COUNT(1) AS c FROM table_median GROUP BY id ) t2 ON (t2.id = t_o.id) ORDER BY t_o.id,val ) o ) a WHERE IF( -- if there is an even number of elements -- take the lower and the upper median -- and use AVG(lower,upper) c MOD 2 = 0, n = c DIV 2 OR n = (c DIV 2)+1, -- if its an odd number of elements -- take the first if its only one element -- or take the one in the middle IF( c = 1, n = 1, n = c DIV 2 + 1 ) ) ) a GROUP BY id; -- Explanation: -- The Statement creates a helper table like -- -- n id val count -- ---------------- -- 1, 1, 1, 7 -- 2, 1, 3, 7 -- 3, 1, 4, 7 -- 4, 1, 5, 7 -- 5, 1, 6, 7 -- 6, 1, 7, 7 -- 7, 1, 8, 7 -- -- 1, 2, 4, 1 -- 1, 3, 2, 2 -- 2, 3, 5, 2 -- -- 1, 4, 1, 4 -- 2, 4, 5, 4 -- 3, 4, 7, 4 -- 4, 4, 12, 4 -- from there we can select the n-th element on the position: count div 2 + 1 

    Costruire la risposta di velcro, per quelli di voi che devono fare una mediana di qualcosa che è raggruppata da un altro parametro:

     SELEZIONA grp_field, t1.val FROM (
        SELECT grp_field, @rownum: = IF (@s = grp_field, @rownum + 1, 0) AS row_number ,
        @s: = IF (@s = grp_field, @s, grp_field) AS sec, d.val
       FROM data d, (SELECT @rownum: = 0, @s: = 0) r
       ORDINA DI grp_field, d.val
     ) come t1 JOIN (
       SELEZIONA grp_field, count (*) come total_rows
       DA dati d
       GROUP BY grp_field
     ) come t2
     ON t1.grp_field = t2.grp_field
     DOVE t1.row_number = floor (total_rows / 2) +1;
    

    La maggior parte delle soluzioni di cui sopra funzionano solo per un campo della tabella, potrebbe essere necessario ottenere la mediana (50 ° percentile) per molti campi nella query.

    Io uso questo:

     SELECT CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(field_name ORDER BY field_name SEPARATOR ','), ',', 50/100 * COUNT(*) + 1), ',', -1) AS DECIMAL) AS `Median` FROM table_name; 

    È ansible sostituire il “50” nell’esempio sopra a qualsiasi percentile, è molto efficiente.

    Assicurati di avere abbastanza memoria per GROUP_CONCAT, puoi cambiarlo con:

     SET group_concat_max_len = 10485760; #10MB max length 

    Ulteriori dettagli: http://web.performancerasta.com/metrics-tips-calculating-95th-99th-or-any-percentile-with-single-mysql-query/

    Potresti usare la funzione definita dall’utente che si trova qui .

    Si prende cura di un conteggio di valori dispari – dà la media dei due valori nel mezzo in quel caso.

     SELECT AVG(val) FROM ( SELECT x.id, x.val from data x, data y GROUP BY x.id, x.val HAVING SUM(SIGN(1-SIGN(IF(y.val-x.val=0 AND x.id != y.id, SIGN(x.id-y.id), y.val-x.val)))) IN (ROUND((COUNT(*))/2), ROUND((COUNT(*)+1)/2)) ) sq 

    Ho questo codice qui sotto che ho trovato su HackerRank ed è piuttosto semplice e funziona in ogni caso.

     SELECT M.MEDIAN_COL FROM MEDIAN_TABLE M WHERE (SELECT COUNT(MEDIAN_COL) FROM MEDIAN_TABLE WHERE MEDIAN_COL < M.MEDIAN_COL ) = (SELECT COUNT(MEDIAN_COL) FROM MEDIAN_TABLE WHERE MEDIAN_COL > M.MEDIAN_COL ); 

    Il mio codice, efficiente senza tabelle o variabili aggiuntive:

     SELECT ((SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(val order by val), ',', floor(1+((count(val)-1) / 2))), ',', -1)) + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(val order by val), ',', ceiling(1+((count(val)-1) / 2))), ',', -1)))/2 as median FROM table; 

    Facoltativamente, puoi anche farlo in una stored procedure:

     DROP PROCEDURE IF EXISTS median; DELIMITER // CREATE PROCEDURE median (table_name VARCHAR(255), column_name VARCHAR(255), where_clause VARCHAR(255)) BEGIN -- Set default parameters IF where_clause IS NULL OR where_clause = '' THEN SET where_clause = 1; END IF; -- Prepare statement SET @sql = CONCAT( "SELECT AVG(middle_values) AS 'median' FROM ( SELECT t1.", column_name, " AS 'middle_values' FROM ( SELECT @row:=@row+1 as `row`, x.", column_name, " FROM ", table_name," AS x, (SELECT @row:=0) AS r WHERE ", where_clause, " ORDER BY x.", column_name, " ) AS t1, ( SELECT COUNT(*) as 'count' FROM ", table_name, " x WHERE ", where_clause, " ) AS t2 -- the following condition will return 1 record for odd number sets, or 2 records for even number sets. WHERE t1.row >= t2.count/2 AND t1.row < = ((t2.count/2)+1)) AS t3 "); -- Execute statement PREPARE stmt FROM @sql; EXECUTE stmt; END// DELIMITER ; -- Sample usage: -- median(table_name, column_name, where_condition); CALL median('products', 'price', NULL); 
     SELECT SUBSTRING_INDEX( SUBSTRING_INDEX( GROUP_CONCAT(field ORDER BY field), ',', (( ROUND( LENGTH(GROUP_CONCAT(field)) - LENGTH( REPLACE( GROUP_CONCAT(field), ',', '' ) ) ) / 2) + 1 )), ',', -1 ) FROM table 

    Quanto sopra sembra funzionare per me.

    Ho usato un approccio a due query:

    • primo per ottenere il conteggio, il minimo, il massimo e il medio
    • la seconda (istruzione preparata) con clausole “LIMIT @ count / 2, 1” e “ORDER BY ..” per ottenere il valore mediano

    Questi sono racchiusi in una funzione defn, quindi tutti i valori possono essere restituiti da una chiamata.

    Se i tuoi intervalli sono statici e i tuoi dati non cambiano spesso, potrebbe essere più efficace precomporre / archiviare questi valori e utilizzare i valori memorizzati invece di eseguire query ogni volta.

    poiché avevo solo bisogno di una soluzione mediana AND percentile, ho realizzato una funzione semplice e abbastanza flessibile basata sui risultati di questo thread. So di essere felice anch’io se trovo le funzioni “readymade” che sono facili da includere nei miei progetti, quindi ho deciso di condividere rapidamente:

     function mysql_percentile($table, $column, $where, $percentile = 0.5) { $sql = " SELECT `t1`.`".$column."` as `percentile` FROM ( SELECT @rownum:=@rownum+1 as `row_number`, `d`.`".$column."` FROM `".$table."` `d`, (SELECT @rownum:=0) `r` ".$where." ORDER BY `d`.`".$column."` ) as `t1`, ( SELECT count(*) as `total_rows` FROM `".$table."` `d` ".$where." ) as `t2` WHERE 1 AND `t1`.`row_number`=floor(`total_rows` * ".$percentile.")+1; "; $result = sql($sql, 1); if (!empty($result)) { return $result['percentile']; } else { return 0; } } 

    L’utilizzo è molto semplice, ad esempio dal mio attuale progetto:

     ... $table = DBPRE."zip_".$slug; $column = 'seconds'; $where = "WHERE `reached` = '1' AND `time` >= '".$start_time."'"; $reaching['median'] = mysql_percentile($table, $column, $where, 0.5); $reaching['percentile25'] = mysql_percentile($table, $column, $where, 0.25); $reaching['percentile75'] = mysql_percentile($table, $column, $where, 0.75); ... 

    Ecco la mia strada. Certo, potresti metterlo in una procedura 🙂

     SET @median_counter = (SELECT FLOOR(COUNT(*)/2) - 1 AS `median_counter` FROM `data`); SET @median = CONCAT('SELECT `val` FROM `data` ORDER BY `val` LIMIT ', @median_counter, ', 1'); PREPARE median FROM @median; EXECUTE median; 

    Puoi evitare la variabile @median_counter , se la sostituisci:

     SET @median = CONCAT( 'SELECT `val` FROM `data` ORDER BY `val` LIMIT ', (SELECT FLOOR(COUNT(*)/2) - 1 AS `median_counter` FROM `data`), ', 1' ); PREPARE median FROM @median; EXECUTE median; 

    La mia soluzione presentata di seguito funziona in una sola query senza creazione di tabella, variabile o anche sotto-query. Inoltre, ti consente di ottenere una mediana per ogni gruppo in query di gruppo (questo è quello che mi serviva!):

     SELECT `columnA`, SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(`columnB` ORDER BY `columnB`), ',', CEILING((COUNT(`columnB`)/2))), ',', -1) medianOfColumnB FROM `tableC` -- some where clause if you want GROUP BY `columnA`; 

    Funziona grazie all’uso intelligente di group_concat e substring_index.

    Per consentire big group_concat, devi impostare group_concat_max_len su un valore più alto (1024 caratteri per impostazione predefinita). Puoi impostarlo così (per la sessione sql corrente):

     SET SESSION group_concat_max_len = 10000; -- up to 4294967295 in 32-bits platform. 

    Maggiori informazioni su group_concat_max_len: https://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_group_concat_max_len

    Un altro riff sulla risposta di Velcrow, ma utilizza una singola tabella intermedia e sfrutta la variabile utilizzata per la numerazione delle righe per ottenere il conteggio, anziché eseguire una query aggiuntiva per calcolarla. Inoltre avvia il conteggio in modo che la prima riga sia la riga 0 per consentire semplicemente di usare Floor e Ceil per selezionare le righe mediane.

     SELECT Avg(tmp.val) as median_val FROM (SELECT inTab.val, @rows := @rows + 1 as rowNum FROM data as inTab, (SELECT @rows := -1) as init -- Replace with better where clause or delete WHERE 2 > 1 ORDER BY inTab.val) as tmp WHERE tmp.rowNum in (Floor(@rows / 2), Ceil(@rows / 2)); 

    Installa e usa queste funzioni statistiche mysql: http://www.xarg.org/2012/07/statistical-functions-in-mysql/

    Successivamente, calcolare la mediana è semplice:

    SELECT mediana (x) FROM t1

    In questo modo sembra includere sia il conteggio pari che dispari senza subquery.

     SELECT AVG(t1.x) FROM table t1, table t2 GROUP BY t1.x HAVING SUM(SIGN(t1.x - t2.x)) = 0 

    Spesso, potremmo aver bisogno di calcolare Median non solo per l’intera tabella, ma per gli aggregati rispetto al nostro ID. In altre parole, calcola la mediana per ogni ID nella nostra tabella, dove ogni ID ha molti record. (buone prestazioni e funziona in molti problemi risolti in SQL +, pari o dispari, più sulle prestazioni dei diversi metodi mediani https://sqlperformance.com/2012/08/t-sql-queries/median )

     SELECT our_id, AVG(1.0 * our_val) as Median FROM ( SELECT our_id, our_val, COUNT(*) OVER (PARTITION BY our_id) AS cnt, ROW_NUMBER() OVER (PARTITION BY our_id ORDER BY our_val) AS rn FROM our_table ) AS x WHERE rn IN ((cnt + 1)/2, (cnt + 2)/2) GROUP BY our_id; 

    Spero che sia d’aiuto

    Se MySQL ha ROW_NUMBER, allora MEDIAN è (sii ispirato da questa query di SQL Server):

     WITH Numbered AS ( SELECT *, COUNT(*) OVER () AS Cnt, ROW_NUMBER() OVER (ORDER BY val) AS RowNum FROM yourtable ) SELECT id, val FROM Numbered WHERE RowNum IN ((Cnt+1)/2, (Cnt+2)/2) ; 

    L’IN viene utilizzato nel caso in cui si disponga di un numero pari di voci.

    Se si desidera trovare la mediana per gruppo, quindi solo il gruppo PARTITION BY nelle clausole OVER.

    rapinare

    Dopo aver letto tutti quelli precedenti non corrispondevano al mio effettivo requisito, quindi ho implementato uno mio che non ha bisogno di alcuna procedura o di dichiarazioni complicate, solo I GROUP_CONCAT tutti i valori della colonna che volevo ottenere MEDIAN e applicare un COUNT DIV Con 2 estrai il valore in mezzo alla lista come la seguente query:

    (POS è il nome della colonna che voglio ottenere la sua mediana)

     (query) SELECT SUBSTRING_INDEX ( SUBSTRING_INDEX ( GROUP_CONCAT(pos ORDER BY CAST(pos AS SIGNED INTEGER) desc SEPARATOR ';') , ';', COUNT(*)/2 ) , ';', -1 ) AS `pos_med` FROM table_name GROUP BY any_criterial 

    Spero che questo possa essere utile per qualcuno nel modo in cui molti altri commenti erano per me da questo sito.

    Conoscendo il numero esatto di righe è ansible utilizzare questa query:

     SELECT  AS VAL FROM  ORDER BY VAL LIMIT 1 OFFSET 

    Dove = ceiling( / 2.0) - 1

    Ho un database contenente circa 1 miliardo di righe che richiediamo per determinare l’età media dell’insieme. Ordinare un miliardo di righe è difficile, ma se si aggregano i valori distinti che si possono trovare (età comprese tra 0 e 100), è ansible ordinare QUESTO elenco e utilizzare alcune magie aritmetiche per trovare il percentile desiderato come segue:

     with rawData(count_value) as ( select p.YEAR_OF_BIRTH from dbo.PERSON p ), overallStats (avg_value, stdev_value, min_value, max_value, total) as ( select avg(1.0 * count_value) as avg_value, stdev(count_value) as stdev_value, min(count_value) as min_value, max(count_value) as max_value, count(*) as total from rawData ), aggData (count_value, total, accumulated) as ( select count_value, count(*) as total, SUM(count(*)) OVER (ORDER BY count_value ROWS UNBOUNDED PRECEDING) as accumulated FROM rawData group by count_value ) select o.total as count_value, o.min_value, o.max_value, o.avg_value, o.stdev_value, MIN(case when d.accumulated >= .50 * o.total then count_value else o.max_value end) as median_value, MIN(case when d.accumulated >= .10 * o.total then count_value else o.max_value end) as p10_value, MIN(case when d.accumulated >= .25 * o.total then count_value else o.max_value end) as p25_value, MIN(case when d.accumulated >= .75 * o.total then count_value else o.max_value end) as p75_value, MIN(case when d.accumulated >= .90 * o.total then count_value else o.max_value end) as p90_value from aggData d cross apply overallStats o GROUP BY o.total, o.min_value, o.max_value, o.avg_value, o.stdev_value ; 

    Questa query dipende dalle funzioni della finestra di supporto db (incluso ROWS UNBOUNDED PRECEDING), ma se non si ha questo è semplice unire aggData CTE con se stesso e aggregare tutti i totali precedenti nella colonna ‘accumulata’ che viene usata per determinare quale il valore contiene il precentile specificato. Il campione sopra calcola p10, p25, p50 (mediana), p75 e p90.

    -Chris

    Tratto da: http://mdb-blog.blogspot.com/2015/06/mysql-find-median-nth-element-without.html

    Vorrei suggerire un altro modo, senza aderire , ma lavorando con le stringhe

    non l’ho controllato con tabelle con grandi dati, ma i tavoli piccoli / medi funziona perfettamente.

    La cosa buona qui, che funziona anche da GROUPING in modo che possa restituire la mediana per diversi elementi.

    ecco il codice di prova per la tabella di test:

     DROP TABLE test.test_median CREATE TABLE test.test_median AS SELECT 'book' AS grp, 4 AS val UNION ALL SELECT 'book', 7 UNION ALL SELECT 'book', 2 UNION ALL SELECT 'book', 2 UNION ALL SELECT 'book', 9 UNION ALL SELECT 'book', 8 UNION ALL SELECT 'book', 3 UNION ALL SELECT 'note', 11 UNION ALL SELECT 'bike', 22 UNION ALL SELECT 'bike', 26 

    e il codice per trovare la mediana per ciascun gruppo:

     SELECT grp, SUBSTRING_INDEX( SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val), ',', COUNT(*)/2 ), ',', -1) as the_median, GROUP_CONCAT(val ORDER BY val) as all_vals_for_debug FROM test.test_median GROUP BY grp 

    Produzione:

     grp | the_median| all_vals_for_debug bike| 22 | 22,26 book| 4 | 2,2,3,4,7,8,9 note| 11 | 11 

    In alcuni casi la mediana viene calcasting come segue:

    La “mediana” è il valore “medio” nella lista dei numeri quando sono ordinati per valore. Per set di conteggi pari, la mediana è la media dei due valori medi . Ho creato un codice semplice per questo:

     $midValue = 0; $rowCount = "SELECT count(*) as count {$from} {$where}"; $even = FALSE; $offset = 1; $medianRow = floor($rowCount / 2); if ($rowCount % 2 == 0 && !empty($medianRow)) { $even = TRUE; $offset++; $medianRow--; } $medianValue = "SELECT column as median {$fromClause} {$whereClause} ORDER BY median LIMIT {$medianRow},{$offset}"; $medianValDAO = db_query($medianValue); while ($medianValDAO->fetch()) { if ($even) { $midValue = $midValue + $medianValDAO->median; } else { $median = $medianValDAO->median; } } if ($even) { $median = $midValue / 2; } return $median; 

    La mediana $ restituita sarebbe il risultato richiesto 🙂

    Mediane raggruppate per dimensione:

     SELECT your_dimension, avg(t1.val) as median_val FROM ( SELECT @rownum:=@rownum+1 AS `row_number`, IF(@dim <> d.your_dimension, @rownum := 0, NULL), @dim := d.your_dimension AS your_dimension, d.val FROM data d, (SELECT @rownum:=0) r, (SELECT @dim := 'something_unreal') d WHERE 1 -- put some where clause here ORDER BY d.your_dimension, d.val ) as t1 INNER JOIN ( SELECT d.your_dimension, count(*) as total_rows FROM data d WHERE 1 -- put same where clause here GROUP BY d.your_dimension ) as t2 USING(your_dimension) WHERE 1 AND t1.row_number in ( floor((total_rows+1)/2), floor((total_rows+2)/2) ) GROUP BY your_dimension; 

    In base alla risposta di @ bob, questo generalizza la query per avere la possibilità di restituire più mediane, raggruppate secondo alcuni criteri.

    Pensa, ad esempio, al prezzo di vendita medio per le auto usate in un lotto di auto, raggruppate per anno-mese.

     SELECT period, AVG(middle_values) AS 'median' FROM ( SELECT t1.sale_price AS 'middle_values', t1.row_num, t1.period, t2.count FROM ( SELECT @last_period:=@period AS 'last_period', @period:=DATE_FORMAT(sale_date, '%Y-%m') AS 'period', IF (@period<>@last_period, @row:=1, @row:=@row+1) as `row_num`, x.sale_price FROM listings AS x, (SELECT @row:=0) AS r WHERE 1 -- where criteria goes here ORDER BY DATE_FORMAT(sale_date, '%Y%m'), x.sale_price ) AS t1 LEFT JOIN ( SELECT COUNT(*) as 'count', DATE_FORMAT(sale_date, '%Y-%m') AS 'period' FROM listings x WHERE 1 -- same where criteria goes here GROUP BY DATE_FORMAT(sale_date, '%Y%m') ) AS t2 ON t1.period = t2.period ) AS t3 WHERE row_num >= (count/2) AND row_num < = ((count/2) + 1) GROUP BY t3.period ORDER BY t3.period;