7

完全に機能している次の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;

ここで、各行のデータの中央値を示す結果列も追加したいと考えています。この下SELECTでは、完璧な世界では次のようになります。

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

Google で MySQL の中央値関数を検索すると、この回答にたどり着きました。これは、テーブル全体のデータ セットの中央値に関心がある場合は問題ないようです 。MySQL で中央値を計算する簡単な方法

ここでの違いは、テーブル内のデータをname列でグループ化し、この列でグループ化されたデータの各行の中央値を取得したいということです。

これを行うための気の利いたトリックを知っている人はいますか?

ありがとう!

4

2 に答える 2

3

メジアン関数が組み込まれていない場合でも、MySQL の GROUP BY でメジアンを計算できます。

次の表を検討してください。

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

各行について、より少ない類似アイテムの数を数えることができます。以下の値の数を数えることもできます。

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

クエリあり

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

中央値は、より小さいか等しい数がアイテム数の半分の場合に発生します

  • アクリントンには 4 個のアイテムがあります。この半分は 2 で、範囲 0..2 (200.00 に相当) と範囲 2..3 (300.00 に相当) にあります。

  • Bullingdonには4つのアイテムがあります。2 の範囲は 1..2 (値 300.00) から 2..3 (値 400.00) です。

  • カーディントンには 5 個のアイテムがあります。値 2.5 は、カーディントン 151 に対応する 2 から 3 の間です。

中央値は、以下によって返される最小値と最大値の平均です。

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

これにより、次のことが得られます。

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

最後に、中央値を取得できます。

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

与える

Acrington   250.000000
Bulingdon   350.000000
Cardington  151.000000
于 2014-05-06T02:07:50.347 に答える
3

これを行う唯一の方法は、文字列操作による
ものです。GROUP_CONCATすべての値のリストが作成され、インデントされSUBSTRING_INDEXた中央値が取得されます

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;  

CASE は、値の数が奇数の 1 つの中央値、または値の数が偶数の 2 つの中央値があるかどうかを確認するために必要です。2 番目のケースでは、中央値は確立された 2 つの値の平均です。

SQLFiddle

于 2014-04-08T10:32:44.983 に答える