| この記事の要点 |
|
パーセンタイルとは・定義の違い
パーセンタイルは、データを小さい順に並べたとき「下から p% の位置にある値」です。50 パーセンタイルは中央値、90 パーセンタイルは「90% のデータがこの値以下」という目安で、レスポンスタイムの監視(p90・p99)などでよく使われます。
ただし計算方法には複数の流儀があり、データ件数が少ないと結果が変わります。
| 方式 | 計算 | 相当する関数 |
|---|---|---|
| 最近順位法(nearest rank) | 件数 N のとき、小さい順で CEIL(p × N) 番目の値。必ず実在する値を返す | PERCENTILE_DISC(MariaDB / PostgreSQL) |
| 線形補間 | 位置 h = 1 + p × (N − 1) を求め、前後 2 つの値を按分する | PERCENTILE_CONT、Excel の PERCENTILE.INC、pandas の quantile() 既定 |
例えば値が 1〜10 の 10 件で 90 パーセンタイルを求めると、最近順位法は 9 番目の「9」、線形補間は h = 9.1 なので「9.1」になります。
MySQL 8.0 以降:ウィンドウ関数で求める(推奨)
最近順位法
WITH ranked AS (
SELECT
val,
ROW_NUMBER() OVER (ORDER BY val) AS rn,
COUNT(*) OVER () AS cnt
FROM test_table
WHERE val IS NOT NULL
)
SELECT MIN(val) AS p90
FROM ranked
WHERE rn >= CEIL(0.9 * cnt);
0.9 を 0.5 にすれば中央値(下側)、0.99 にすれば 99 パーセンタイルです。
線形補間(PERCENTILE_CONT と同じ結果)
WITH ranked AS (
SELECT
val,
ROW_NUMBER() OVER (ORDER BY val) AS rn,
COUNT(*) OVER () AS cnt
FROM test_table
WHERE val IS NOT NULL
),
pos AS (
SELECT 1 + 0.9 * (MAX(cnt) - 1) AS h FROM ranked
)
SELECT
MAX(CASE WHEN rn = FLOOR(h) THEN val END)
+ (h - FLOOR(h))
* (MAX(CASE WHEN rn = CEIL(h) THEN val END)
- MAX(CASE WHEN rn = FLOOR(h) THEN val END)) AS p90
FROM ranked
CROSS JOIN pos
GROUP BY h;
グループごとに求める
カテゴリ別・日別などのパーセンタイルは、ウィンドウ関数に PARTITION BY を付けます。
WITH ranked AS (
SELECT
category,
val,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY val) AS rn,
COUNT(*) OVER (PARTITION BY category) AS cnt
FROM test_table
WHERE val IS NOT NULL
)
SELECT category, MIN(val) AS p90
FROM ranked
WHERE rn >= CEIL(0.9 * cnt)
GROUP BY category;
MySQL 5.7 以前:GROUP_CONCAT と SUBSTRING_INDEX で求める
ウィンドウ関数がないバージョンでは、値を小さい順にカンマ区切りで連結し、N 番目の要素を切り出す方法が使えます。以下の SQL を実行することで、MySQL でパーセンタイルを導き出すことができます(90% を指定した例)。
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(
t1.val ORDER BY t1.val SEPARATOR ','
)
, ',', 90 / 100 * COUNT(*) + 1
), ',', - 1
) AS `Percentile`
FROM
test_table AS t1
内側の SUBSTRING_INDEX が「先頭から指定した個数の要素」を取り出し、外側の SUBSTRING_INDEX(..., ',', -1) がその最後の 1 つを取り出す仕組みです。
上の式は旧版の本記事で紹介していたもので、「p × N + 1 番目」を返します。p × N が整数になる場合(10 件で 90% など)は最近順位法より 1 つ上の値になります。定義をそろえるなら、次のように NULL を除外して CEIL を使います。
SET SESSION group_concat_max_len = 1000000; -- 切り捨て防止(先に必ず実行)
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(t1.val ORDER BY t1.val SEPARATOR ','),
',', CEIL(0.9 * COUNT(t1.val))
), ',', -1
) AS p90
FROM test_table AS t1
WHERE t1.val IS NOT NULL;
落とし穴
group_concat_max_lenによる切り捨て: 既定は 1024 バイトで、超えた分は警告だけ出して切り捨てられる。件数が多いと後半の値が消え、エラーにならないまま誤ったパーセンタイルが返る。セッション変数を十分大きくするか、ウィンドウ関数の方法を使う(上限はmax_allowed_packetにも制約される)- NULL:
GROUP_CONCATは NULL を連結しないがCOUNT(*)は数えるため、位置がずれる。COUNT(列)かWHERE 列 IS NOT NULLを使う - 戻り値が文字列:
SUBSTRING_INDEXの結果は文字列。数値として比較・計算するならCAST(... AS DECIMAL(10,2))などで変換する - パフォーマンス: どちらの方法も全件の並べ替えが必要。大きなテーブルでは対象期間などで絞り込み、
ORDER BYの列にインデックスを用意する - NTILE や PERCENT_RANK との混同:
NTILE(100)は行を 100 グループに分ける関数、PERCENT_RANK()は各行が何パーセンタイルの位置にあるかを返す関数で、「p% 点の値」を直接返すものではない
確認方法
SELECT VERSION();で MySQL のバージョンを確認し、8.0 以上ならウィンドウ関数の方法を使う- 1〜10 のような結果が分かっているデータを一時テーブルに入れて実行し、期待値(最近順位法なら 9、線形補間なら 9.1)と一致するか確かめる
- GROUP_CONCAT 方式では、実行後に
SHOW WARNINGS;で「cut by GROUP_CONCAT()」の警告が出ていないか確認する - 同じデータを Excel(PERCENTILE.INC)や pandas(
quantile(0.9))で計算し、線形補間の結果と照合する
関連
子ページ
子ページはありません
同階層のページ
- ダウンロード&インストール方法(Windows)
- インストール方法(Linux)
- コマンド一覧
- SQL
- データ型
- 関数
- 管理ツール
- 設定
- パフォーマンスチューニング関連
- エクスポートおよびインポート
- エラー&トラブル
- 文字コードの確認
- 実行中の SQL の状態確認およびプロセスキルの方法
- パスワードの無効化設定
- root ユーザーの初期パスワード確認方法
- rootユーザーのパスワード変更方法
- LIMIT, OFFSET の始まりと挙動
- mysqlのバージョン確認方法
- MySQLで実行計画を表示する方法
- レプリケーションのステータス確認方法
- 中央値の導き方(バージョン8未満)
- 階層SQL(バージョン8未満)
- パーセンタイルの導き方
- 特定スキーマの全テーブルの全カラム情報を取得する方法
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38