2013-04-30 11 views
7

Mam następującą kwerendę MySQL, które działa perfekcyjnie:Korzystanie mediana wzdłuż bocznych funkcji MAX, MIN i AVG w MySQL

select 
    count(*) as `# of Data points`, 
    name, 
    max((QNTY_Sell/QNTYDelivered)*1000) as `MAX Thousand Price`, 
    min((QNTY_Sell/QNTYDelivered)*1000) as `MIN Thousand Price`, 
    avg((QNTY_Sell/QNTYDelivered)*1000) as `MEAN Thousand Price` 
from 
    table_name 
where 
    year(date) >= 2012 and 
    name like "%the_name%" and 
    QNTYDelivered > 0 and 
    QNTY_Sell > 0 
group by name 
order by name; 

Teraz Pragnę również dodać kolumnę wynik, który daje mi mediana dane dla każdej linii. Pod SELECT miałoby to wyglądać w idealnym świecie:

median((QNTY_Sell/QNTYDelivered)*1000) as `MEDIAN Thousand Price` 

wyszukiwania Google dla MySQL medianą funkcji przyniósł mi do tej odpowiedzi, co wydaje się ok, jeśli jesteś zainteresowany mediany zestawu danych dla całej tabeli : Simple way to calculate median with MySQL

Różnica polega na tym, że grupuję dane w mojej tabeli przez kolumnę name i chcę uzyskać medianę dla każdej linii danych pogrupowanych według tej kolumny.

Czy ktoś zna fajną sztuczkę, która pozwoli mi to zrobić?

Dzięki!

+0

nie ma funkcji mediany w mysql –

+3

Czy musi być w tym samym zapytaniu? Jeśli możesz uruchomić drugie zapytanie, możesz obliczyć punkt środkowy zestawu, gdy znasz już liczbę punktów danych. Środek jest jednym lub dwoma rzędami. Przeprowadź to samo zapytanie, ale dodaj LIMIT [środek], [środek punktu 2] i zwróć średnią. –

+0

Będziesz musiał zmienić kolejność, wykonując powyższe czynności - zakładam, że chcesz uzyskać medianę QNTY_Sell/QNTYDelivered, więc musisz zamówić to, aby znaleźć punkt środkowy. – nickL

Odpowiedz

2

Jedynym sposobem znalazłem to zrobić poprzez manipulację strun:
z GROUP_CONCAT wykaz wszystkich wartości utworzonego wówczas z wcięciami SUBSTRING_INDEX mediany pochodzi

SELECT 
    count(*) AS `# of Data points`, 
    name, 
    max((QNTY_Sell/QNTYDelivered)*1000) AS `MAX Thousand Price`, 
    min((QNTY_Sell/QNTYDelivered)*1000) AS `MIN Thousand Price`, 
    avg((QNTY_Sell/QNTYDelivered)*1000) AS `MEAN Thousand Price` 
    , CASE (count(*) % 2) 
    WHEN 1 THEN SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', (count(*) + 1)/2) 
    , ',', -1) 
    ELSE (SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', count(*)/2) 
    , ',', -1) 
    + SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', (count(*) + 1)/2) 
    , ',', -1))/2 
    END median 
FROM 
    sales 
WHERE 
    year(date) >= 2012 AND 
    name LIKE "%art.%" AND 
    QNTYDelivered > 0 AND 
    QNTY_Sell > 0 
GROUP BY name 
ORDER BY name; 

jest potrzebne w sprawie, aby sprawdzić jeśli mamy pojedynczą wartość mediany, z nieparzystą liczbą wartości lub dwiema wartościami mediany, z parzystą liczbą wartości, w drugim przypadku mediana jest średnią dwóch założonych wartości.

SQLFiddle

+0

Patrząc na tę bestię rozwiązania, naprawdę - jak, NAPRAWDĘ - zastanawiam się, dlaczego platformy SQL zbiorowo odmawiają implementacji funkcji mediany i trybu. Mając AVG, MIN, MAX, ale nie dwa pozostałe są całkiem na wpół zagracone ... – Tomm

+0

Nie zrozum mnie źle, pochwalam Cię za twoje rozwiązanie. Ale to zabawne, że musimy wymyślić wielkość kolekcji dzieł Dostojewskiego tylko po to, by zdobyć tę krwawą medianę. Skończyłem tutaj, ponieważ potrzebuję uzyskać medianę i tryb w kontekście kilku zagnieżdżonych podzapytań; i uświadomienie sobie, że tak trudno uzyskać coś tak pozornie banalnego, jak Median jest nieco irytujące. – Tomm

+0

Hej, zaimplementowałem twoje rozwiązanie, ale zauważyłem, że Mediana parzystej liczby wartości jest "wyłączona o jedną jednostkę", tj. W tablicy z 10 wartościami, twoje rozwiązanie umieszcza medianę między czwartą a piątą wartością, zamiast piątą. i 6. Naprawiłem to za pomocą '(count (*)/2) + 1' w dwóch wywołaniach' SUBSTRING_INDEX' instrukcji 'ELSE'. – Tomm

3

Można obliczyć medianę z GROUP BY w MySQL, chociaż nie ma mediana funkcja zbudowany w

Rozważmy tabelę.

Acrington 200.00 
Acrington 200.00 
Acrington 300.00 
Acrington 400.00 
Bulingdon 200.00 
Bulingdon 300.00 
Bulingdon 400.00 
Bulingdon 500.00 
Cardington 100.00 
Cardington 149.00 
Cardington 151.00 
Cardington 300.00 
Cardington 300.00 

Dla każdego wiersza ty może policzyć liczbę podobnych przedmiotów, które są mniejsze. Można też policzyć ile wartości są mniejsze lub równe:

name  v  < <= 
Acrington 200.00 0 2 
Acrington 200.00 0 2 
Acrington 300.00 2 3 
Acrington 400.00 3 4 
Bulingdon 200.00 0 1 
Bulingdon 300.00 1 2 
Bulingdon 400.00 2 3 
Bulingdon 500.00 3 4 
Cardington 100.00 0 1 
Cardington 149.00 1 2 
Cardington 151.00 2 3 
Cardington 300.00 3 5 
Cardington 300.00 3 5 

Przy zapytaniu

SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
      , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
    FROM sale o 

Wartość mediany nastąpi, gdy mniej niż lub równy liczba to połowa liczby przedmiotów

  • Acrington ma 4 pozycje. Połowa z tego jest 2, która jest w zakresie 0..2 (co odpowiada 200,00), a także w zakresie 2..3 (co odpowiada 300,00)

  • Bullingdon posiada również 4 elementy. 2 jest w zakresie 1..2 (wartość 300,00) i 2..3 (wartość 400,00)

  • Cardington ma 5 artykułów. Wartość 2,5 wynosi od 2 do 3, co odpowiada Cardington 151.

Wartość średnia jest średnią z min i max wartości zwracane przez:

SELECT cs.name,v 
    FROM 
    (SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
       , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
     FROM sale o) cs JOIN 
    (SELECT name,COUNT(1)*.5 as cn 
     FROM sale 
     GROUP BY name) cc ON cs.name=cc.name 
WHERE cn between ls and lse 

co daje:

Acrington 200.00 
Acrington 200.00 
Acrington 300.00 
Bulingdon 300.00 
Bulingdon 400.00 
Cardington 151.00 

Wreszcie możemy uzyskać mediana

SELECT name,(MAX(v)+MIN(v))/2 FROM 
(SELECT cs.name,v 
    FROM 
    (SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
       , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
     FROM sale o) cs JOIN 
    (SELECT name,COUNT(1)*.5 as cn 
     FROM sale 
    GROUP BY name) cc ON cs.name=cc.name 
WHERE cn between ls and lse 
) AS medians 
GROUP BY name 

Podanie

Acrington 250.000000 
Bulingdon 350.000000 
Cardington 151.000000 
Powiązane problemy