236

MySQL で中央値を計算する最も簡単な (そしてできれば遅すぎない) 方法は何ですか? 私はAVG(x)平均を見つけるために使用しましたが、中央値を計算する簡単な方法を見つけるのに苦労しています. 今のところ、すべての行を PHP に返し、並べ替えを行ってから中央の行を選択していますが、単一の MySQL クエリでそれを行う簡単な方法がきっとあるはずです。

サンプルデータ:

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

でソートするとvalが得られる2 2 3 4 7 8 9ため、中央値は であり4、対SELECT AVG(val)which ==5です。

4

47 に答える 47

252

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は、最初のパスの後、@rownum に行の総数が含まれることを指摘しています。これは中央値を決定するために使用できるため、2 回目のパスや結合は必要ありません。

またAVG(dd.val)dd.row_number IN(...)偶数のレコードがある場合に中央値を正しく生成するために使用されます。理由:

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

最後に、MariaDB 10.3.3+ には MEDIAN 関数が含まれています

于 2011-08-31T21:53:36.790 に答える
70

コメントでオンラインで別の回答を見つけました:

ほぼすべての 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

列のインデックスが適切に作成され、そのインデックスがフィルター処理と並べ替えに使用されていることを確認してください。説明計画で確認します。

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

「中央値」の行数を計算します。多分使用:median_row = floor(count / 2)

次に、リストから選択します。

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

これにより、必要な値だけを含む 1 つの行が返されます。

ジェイコブ

于 2009-08-18T01:02:51.077 に答える
35

受け入れられた解決策が私のMySQLインストールでは機能せず、空のセットを返すことがわかりましたが、このクエリは私がテストしたすべての状況で機能しました:

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
于 2012-06-04T06:59:33.310 に答える
27

残念ながら、TheJacobTaylor の回答も velcrow の回答も、現在のバージョンの MySQL に対して正確な結果を返しません。

上記のベルクロの答えは近いですが、行数が偶数の結果セットについては正しく計算されません。中央値は、1) 奇数セットの中央値、または 2) 偶数セットの 2 つの中央値の平均として定義されます。

したがって、奇数セットと偶数セットの両方を処理するためにパッチを適用したベルクロのソリューションは次のとおりです。

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;

これを使用するには、次の 3 つの簡単な手順に従います。

  1. 上記のコードの「median_table」(2 回出現)をテーブルの名前に置き換えます
  2. "median_column" (3 回) を、中央値を求めたい列名に置き換えます。
  3. WHERE 条件がある場合は、"WHERE 1" (2 回出現) を where 条件に置き換えます。
于 2013-05-21T22:03:02.433 に答える
13

より速い方法を提案します。

行数を取得します。

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

次に、ソートされたサブクエリで中央の値を取得します。

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

乱数の5x10e6データセットでこれをテストしたところ、10秒以内に中央値が見つかりました。

于 2011-05-31T00:05:20.840 に答える
11

この mysql 統計関数をインストールして使用します: http://www.xarg.org/2012/07/statistical-functions-in-mysql/

その後、中央値を計算するのは簡単です:

SELECT median(val) FROM data;
于 2014-09-12T14:18:03.833 に答える
8

MySQL ドキュメントのこのページのコメントには、次の提案があります。

-- (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 o.c AS shifted_n,
IF(@a mod o.c=0, o.c, @a) AS n,
o.id,
o.val,
-- the number of elements for every id
o.c
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 
于 2009-08-18T00:20:48.510 に答える
7

私はHackerRankで見つけた以下のコードを持っています。それは非常にシンプルで、あらゆるケースで機能します.

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 );
于 2017-06-13T13:17:26.193 に答える
6

上記のソリューションのほとんどは、テーブルの 1 つのフィールドに対してのみ機能します。クエリの多くのフィールドの中央値 (50 パーセンタイル) を取得する必要がある場合があります。

私はこれを使用します:

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;

上記の例の「50」を任意のパーセンタイルに置き換えることができ、非常に効率的です。

GROUP_CONCAT に十分なメモリがあることを確認してください。次のように変更できます。

SET group_concat_max_len = 10485760; #10MB max length

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

于 2013-08-13T11:33:31.003 に答える
4

別のパラメーターでグループ化されたものから中央値を計算する必要がある人のために、ベルクロの答えから構築します。

SELECT 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 秒, d.val
  FROM データ d, (SELECT @rownum:=0, @s:=0) r
  ORDER BY grp_field、d.val
) として t1 JOIN (
  SELECT grp_field, count(*) as total_rows
  FROM データ d
  GROUP BY grp_field
) として t2
ON t1.grp_field = t2.grp_field
WHERE t1.row_number=floor(total_rows/2)+1;

于 2012-03-17T23:49:46.603 に答える
4

ここにあるユーザー定義関数を使用できます。

于 2009-08-18T00:19:52.860 に答える
4

MySQL に ROW_NUMBER がある場合、MEDIAN は次のようになります (この 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)
;

IN は、エントリの数が偶数の場合に使用されます。

グループごとの中央値を見つけたい場合は、OVER 句で PARTITION BY グループだけを使用します。

ロブ

于 2009-08-18T00:51:20.207 に答える
3

テーブルや追加の変数なしで効率的な私のコード:

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;
于 2013-04-23T01:07:14.477 に答える
3

奇数の値のカウントを処理します - その場合、中央の 2 つの値の平均を示します。

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
于 2010-11-17T18:21:29.980 に答える
3

完全な中央値をアーカイブする単一のクエリ:

SELECT 
COUNT(*) as total_rows, 
IF(count(*)%2 = 1, CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val SEPARATOR ','), ',', 50/100 * COUNT(*)), ',', -1) AS DECIMAL), ROUND((CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val SEPARATOR ','), ',', 50/100 * COUNT(*) + 1), ',', -1) AS DECIMAL) + CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val SEPARATOR ','), ',', 50/100 * COUNT(*)), ',', -1) AS DECIMAL)) / 2)) as median, 
AVG(val) as average 
FROM 
data
于 2020-05-27T10:36:49.247 に答える
2
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

上記は私のために働くようです。

于 2016-11-22T22:02:27.450 に答える
2

Velcrowの答えの別のリフですが、単一の中間テーブルを使用し、行番号付けに使用される変数を利用してカウントを取得し、追加のクエリを実行して計算します。また、最初の行が行 0 になるようにカウントを開始し、単純に Floor と Ceil を使用して中央の行を選択できるようにします。

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));
于 2014-08-12T18:30:11.680 に答える
2

必要に応じて、ストアド プロシージャでこれを行うこともできます。

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);
于 2013-05-21T23:24:05.003 に答える
1

2 つのクエリ アプローチを使用しました。

  • カウント、最小、最大、平均を取得する最初のもの
  • 中央値を取得するための "LIMIT @count/2, 1" 句と "ORDER BY .." 句を含む 2 つ目 (準備済みステートメント)

これらは関数 defn にラップされているため、1 回の呼び出しですべての値を返すことができます。

範囲が静的で、データが頻繁に変更されない場合は、毎回最初からクエリを実行するのではなく、これらの値を事前に計算して保存し、保存された値を使用する方が効率的かもしれません。

于 2010-07-14T18:26:50.333 に答える
1

@bobの回答に基づいて、これはクエリを一般化し、いくつかの基準でグループ化された複数の中央値を返すことができるようにします。

たとえば、中古車の販売価格の中央値を、年月ごとにグループ化したものを考えてみてください。

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;
于 2016-11-23T01:31:45.577 に答える
1

これが私のやり方です。もちろん、それをプロシージャに入れることもできます:-)

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;

変数を代入すると、変数を回避でき@median_counterます。

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;
于 2014-03-25T17:06:09.987 に答える
1

中央値とパーセンタイルのソリューションが必要だったので、このスレッドの調査結果に基づいて、シンプルで非常に柔軟な関数を作成しました。自分のプロジェクトに簡単に組み込むことができる「既製の」関数を見つけたら、自分自身が幸せであることを知っているので、すぐに共有することにしました。

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;
    }

}

使用法は非常に簡単です。私の現在のプロジェクトの例:

...
$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);
...
于 2013-06-21T09:19:53.503 に答える
1

MySQL で中央値を計算する簡単な方法

set @ct := (select count(1) from station);
set @row := 0;

select avg(a.val) as median from 
(select * from  table order by val) a
where (select @row := @row + 1)
between @ct/2.0 and @ct/2.0 +1;
于 2020-10-22T19:11:32.750 に答える
0

セット内の年齢の中央値を決定するために必要な約 10 億行を含むデータベースがあります。10 億の行を並べ替えるのは難しいですが、見つけることができる個別の値 (年齢の範囲は 0 から 100 まで) を集計する場合は、このリストを並べ替えて、算術魔法を使用して、次のように必要なパーセンタイルを見つけることができます。

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
;

このクエリは、ウィンドウ関数 (ROWS UNBOUNDED PRECEDING を含む) をサポートするデータベースに依存しますが、それがない場合は、aggData CTE をそれ自体と結合し、以前のすべての合計を「累積」列に集計するのは簡単なことです。 value には、指定されたパーセンタイルが含まれます。上記のサンプルは、p10、p25、p50 (中央値)、p75、および p90 を計算します。

-クリス

于 2015-06-17T04:53:16.547 に答える
0

これらのメソッドは、同じテーブルから 2 回選択します。ソース データが負荷の高いクエリから取得されたものである場合、これはクエリを 2 回実行しないようにする方法です。

select KEY_FIELD, AVG(VALUE_FIELD) MEDIAN_VALUE
from (
    select KEY_FIELD, VALUE_FIELD, RANKF
    , @rownumr := IF(@prevrowidr=KEY_FIELD,@rownumr+1,1) RANKR
    , @prevrowidr := KEY_FIELD
    FROM (
        SELECT KEY_FIELD, VALUE_FIELD, RANKF
        FROM (
            SELECT KEY_FIELD, VALUE_FIELD 
            , @rownumf := IF(@prevrowidf=KEY_FIELD,@rownumf+1,1) RANKF
            , @prevrowidf := KEY_FIELD     
            FROM (
                SELECT KEY_FIELD, VALUE_FIELD 
                FROM (
                    -- some expensive query
                )   B
                ORDER BY  KEY_FIELD, VALUE_FIELD
            ) C
            , (SELECT @rownumf := 1) t_rownum
            , (SELECT @prevrowidf := '*') t_previd
        ) D
        ORDER BY  KEY_FIELD, RANKF DESC
    ) E
    , (SELECT @rownumr := 1) t_rownum
    , (SELECT @prevrowidr := '*') t_previd
) F
WHERE RANKF-RANKR BETWEEN -1 and 1
GROUP BY KEY_FIELD
于 2016-12-08T11:17:28.583 に答える
0

場合によっては、中央値は次のように計算されます。

「中央値」は、数値を値順に並べたときの数値のリストの「中間」の値です。偶数セットの場合、中央値は 2 つの中間値の平均です。そのための簡単なコードを作成しました:

$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;

返された $median が必要な結果になります:-)

于 2015-07-15T10:54:35.470 に答える
0

次元別にグループ化された中央値:

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;
于 2015-08-04T16:18:36.020 に答える
0

この方法では、サブクエリなしで偶数カウントと奇数カウントの両方が含まれているようです。

SELECT AVG(t1.x)
FROM table t1, table t2
GROUP BY t1.x
HAVING SUM(SIGN(t1.x - t2.x)) = 0
于 2016-11-01T04:18:47.517 に答える
0

正確な行数がわかれば、次のクエリを使用できます。

SELECT <value> AS VAL FROM <table> ORDER BY VAL LIMIT 1 OFFSET <half>

どこ<half> = ceiling(<size> / 2.0) - 1

于 2014-09-02T09:45:09.190 に答える
0
create table med(id integer);
insert into med(id) values(1);
insert into med(id) values(2);
insert into med(id) values(3);
insert into med(id) values(4);
insert into med(id) values(5);
insert into med(id) values(6);

select (MIN(count)+MAX(count))/2 from 
(select case when (select count(*) from 
med A where A.id<B.id)=(select count(*)/2 from med) OR 
(select count(*) from med A where A.id>B.id)=(select count(*)/2 
from med) then cast(B.id as float)end as count from med B) C;

 ?column? 
----------
  3.5
(1 row)

また

select cast(avg(id) as float) from 
(select t1.id from med t1 JOIN med t2 on t1.id!= t2.id 
group by t1.id having ABS(SUM(SIGN(t1.id-t2.id)))=1) A;
于 2017-03-05T01:12:47.370 に答える
0

ウィンドウ関数 row_number() を使用して、クエリに答えてメディアを見つけることができます

select val 
from (select val, row_number() over (order by val) as rownumber, x.cnt 
from data, (select count(*) as cnt from data) x) abc
where rownumber=ceil(cnt/2);
于 2022-02-23T20:15:02.890 に答える
0

このソリューションのパフォーマンスをここに投稿された残りの回答と比較したことはありませんが、これが最も理解しやすく、中央値を計算するための数式の全範囲をカバーしていることがわかりました。つまり、このソリューションは、偶数番号と奇数番号のデータセットに対して十分に堅牢になります。

SELECT CASE 
-- odd-numbered data sets:
WHEN MOD(COUNT(*), 2) = 1 THEN (SELECT median.<value> AS median
FROM
(SELECT t1.<value>
  FROM (SELECT <value>, 
               ROW_NUMBER() OVER(ORDER BY <value>) AS rownum
          FROM <data>) t1,
       (SELECT COUNT(*) AS num_records FROM <data>) t2
 WHERE t1.rownum =(t2.num_records) / 2) as median)
-- even-numbered data sets:
ELSE (select (low_bound.<value> + up_bound.<value>) / 2 AS median
FROM
(SELECT t1.<value>
  FROM (SELECT <value>, 
               ROW_NUMBER() OVER(ORDER BY <value>) AS rownum
          FROM <data>) t1,
       (SELECT COUNT(*) AS num_records FROM <data>) t2
 WHERE t1.rownum =(t2.num_records - 1) / 2) as low_bound,
 (SELECT t1.<value>
  FROM (SELECT <value>, 
               ROW_NUMBER() OVER(ORDER BY <value>) AS rownum
          FROM station) t1,
       (SELECT COUNT(*) AS num_records FROM data) t2
 WHERE t1.rownum =(t2.num_records + 1) / 2) as up_bound)
END
FROM <data>
于 2021-05-31T02:30:31.770 に答える
0

以下のクエリは、偶数行または奇数行の両方で完璧に機能します。サブクエリでは、前後の行数が同じ値を見つけています。奇数行の場合、having 句は 0 と評価されます (前後の行数が同じであれば、符号は相殺されます)。

同様に、偶数行の場合、having 句は 2 つの行 (中央の 2 行) に対して 1 と評価されます。これは、前後の行数が (まとめて) 同じであるためです。

外側のクエリでは、単一の値 (奇数行の場合) または (偶数行の場合は 2 つの値) のいずれかを平均化します。

select avg(val) as median
from
(
    select d1.val
    from data d1 cross join data d2
    group by d1.val
    having abs(sum(sign(d1.val-d2.val))) in (0,1)
) sub

注: テーブルに重複する値がある場合は、上記の having 句を以下の条件に変更する必要があります。この場合、元の可能性である 0,1 以外の値が存在する可能性があります。以下の条件は、この条件を動的にし、重複の場合にも機能します。

having sum(case when d1.val=d2.val then 1 else 0 end)>=
abs(sum(sign(d1.val-d2.val)))
于 2020-11-07T18:14:11.783 に答える
0

テーブル ステーションと列 lat_n の場合、中央値を取得する MySQL コードは次のとおりです。

set @rows := (select count(1) from station);
set @v1 := 0;
set @sql1 := concat('select lat_n into @v1 from station order by lat_n asc limit 1 offset ', ceil(@rows/2) - 1);
prepare statement1 from @sql1;
execute statement1;
set @v2 := 0;
set @sql2 := concat('select lat_n into @v2 from station order by lat_n asc limit 1 offset ', ceil((@rows + 1)/2) - 1);
prepare statement2 from @sql2;
execute statement2;
select (@v1 + @v2)/2;
于 2021-09-23T12:46:01.373 に答える
-1
set @r = 0;

select  
    case when mod(c,2)=0 then round(sum(lat_N),4)
    else round(sum(lat_N)/2,4) 
    end as Med  
from 
    (select lat_N, @r := @r+1, @r as id from station order by lat_N) A
    cross join
    (select (count(1)+1)/2 as c from station) B
where id >= floor(c) and id <=ceil(c)
于 2016-06-25T10:59:06.787 に答える