数億件規模のテーブルで COUNT(DISTINCT) や PERCENTILE_DISC を実行すると、非常に時間がかかることがあります。近似クエリ(Approximate Query Processing)は、厳密な正確値ではなく「誤差の保証された近似値」を超高速で返します。
Oracle 12.2 以降で提供される APPROX_COUNT_DISTINCT・APPROX_PERCENTILE などの近似集計関数は、確率的データ構造(HyperLogLog・T-Digest など)を使って数秒以内に信頼性の高い近似値を返せます。リアルタイムダッシュボード・大規模ログ分析・探索的データ分析(EDA)で活躍します。
この記事でわかること
- 近似クエリが必要な場面と正確クエリとの使い分け
- APPROX_COUNT_DISTINCT で高速なカーディナリティ推定を行う方法
- APPROX_COUNT_DISTINCT_DETAIL / AGG でブロック単位の事前集計を活用する方法
- APPROX_PERCENTILE でパーセンタイル値を高速計算する方法
- APPROX_MEDIAN・APPROX_SUM・APPROX_COUNT(Oracle 19c 以降)の使い方
- 誤差率の考え方と近似クエリが許容できるユースケース
近似クエリと正確クエリの比較
| 関数(正確) | 関数(近似) | 導入バージョン | 誤差の目安 |
|---|---|---|---|
COUNT(DISTINCT col) |
APPROX_COUNT_DISTINCT(col) |
Oracle 12.2 | ほぼ 0〜2 %(データ量に依存) |
PERCENTILE_DISC(0.5) WITHIN GROUP |
APPROX_PERCENTILE(0.5) |
Oracle 12.2 | 百分位ランク誤差 0.5 % 以内 |
MEDIAN(col) |
APPROX_MEDIAN(col) |
Oracle 19c | APPROX_PERCENTILE(0.5) と同等 |
SUM(col) |
APPROX_SUM(col) |
Oracle 19c | APPROX_RANK 使用時の補助関数 |
COUNT(*) |
APPROX_COUNT(col) |
Oracle 19c | APPROX_RANK 使用時の補助関数 |
APPROX_COUNT_DISTINCT でカーディナリティを高速推定する
APPROX_COUNT_DISTINCT の基本的な使い方
-- APPROX_COUNT_DISTINCT: COUNT(DISTINCT) の近似値を返す
-- 大規模テーブルで COUNT(DISTINCT) より大幅に速い
-- 正確な COUNT(DISTINCT)(大規模テーブルでは重い)
SELECT COUNT(DISTINCT customer_id) AS exact_count
FROM orders;
-- 近似 COUNT(DISTINCT)(同等の結果をはるかに速く返す)
SELECT APPROX_COUNT_DISTINCT(customer_id) AS approx_count
FROM orders;
-- 数億件でも数秒以内に結果を返すことが多い
-- GROUP BY との組み合わせ
SELECT
product_category,
COUNT(*) AS order_count,
APPROX_COUNT_DISTINCT(customer_id) AS unique_customers,
APPROX_COUNT_DISTINCT(product_id) AS unique_products
FROM orders
WHERE order_date >= DATE '2024-01-01'
GROUP BY product_category
ORDER BY unique_customers DESC;
-- 月別のユニークユーザー数を集計する(ダッシュボード向け)
SELECT
TRUNC(order_date, 'MM') AS month,
APPROX_COUNT_DISTINCT(customer_id) AS monthly_unique_customers
FROM orders
GROUP BY TRUNC(order_date, 'MM')
ORDER BY month;
APPROX_COUNT_DISTINCT_DETAIL と APPROX_COUNT_DISTINCT_AGG
Oracle 12.2 以降では
Oracle 12.2 以降では
APPROX_COUNT_DISTINCT_DETAIL でブロック単位の中間集計をBLOB として格納しておき、後から APPROX_COUNT_DISTINCT_AGG で再集計できます。毎日の集計結果を保存しておき、月次・年次の APPROX_COUNT_DISTINCT を生データに戻らずに計算するマテリアライズドビュー的な活用が可能です。
APPROX_COUNT_DISTINCT_DETAIL で事前集計して高速再集計する
-- ステップ 1: 日次サマリーテーブルに中間集計(スケッチ)を格納する
CREATE TABLE daily_customer_sketch AS
SELECT
TRUNC(order_date, 'DD') AS order_day,
product_category,
APPROX_COUNT_DISTINCT_DETAIL(customer_id) AS cust_sketch -- BLOB として格納
FROM orders
GROUP BY TRUNC(order_date, 'DD'), product_category;
-- ステップ 2: スケッチから月次・週次を再集計する(生データへの再スキャン不要)
SELECT
TRUNC(order_day, 'MM') AS month,
product_category,
APPROX_COUNT_DISTINCT_AGG(cust_sketch) AS monthly_unique_customers
FROM daily_customer_sketch
GROUP BY TRUNC(order_day, 'MM'), product_category
ORDER BY month, product_category;
-- 生データに戻らずに月次集計が可能 → 大幅な高速化
APPROX_PERCENTILE でパーセンタイル値を高速計算する
APPROX_PERCENTILE は PERCENTILE_DISC の近似版です。大量のデータに対してパーセンタイル値(中央値・95パーセンタイルなど)を高速に計算できます。
APPROX_PERCENTILE の使い方
-- APPROX_PERCENTILE: パーセンタイル値の近似値を返す
-- 構文: APPROX_PERCENTILE(percentile_value) WITHIN GROUP (ORDER BY col)
-- 中央値(50パーセンタイル)を取得する
SELECT
department_id,
APPROX_PERCENTILE(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary,
APPROX_PERCENTILE(0.9) WITHIN GROUP (ORDER BY salary) AS p90_salary,
APPROX_PERCENTILE(0.99) WITHIN GROUP (ORDER BY salary) AS p99_salary
FROM employees
GROUP BY department_id
ORDER BY department_id;
-- 正確な PERCENTILE_DISC との比較
SELECT
APPROX_PERCENTILE(0.5) WITHIN GROUP (ORDER BY salary) AS approx_median,
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY salary) AS exact_median,
APPROX_PERCENTILE(0.95) WITHIN GROUP (ORDER BY salary) AS approx_p95,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY salary) AS exact_p95
FROM employees;
-- 大規模データでは APPROX_PERCENTILE が大幅に速い
-- APPROX_MEDIAN(Oracle 19c 以降): APPROX_PERCENTILE(0.5) と同等
-- SELECT APPROX_MEDIAN(salary) FROM employees GROUP BY department_id;
-- DET パラメータ: 誤差限界を指定する(デフォルト: 'ERROR_RATE '0.5'')
SELECT
APPROX_PERCENTILE(0.95, 'ERROR_RATE 0.1')
WITHIN GROUP (ORDER BY response_time_ms) AS p95_fast
FROM web_request_logs
WHERE request_date >= DATE '2024-01-01';
-- ERROR_RATE: パーセンタイルランクの最大誤差(0.1 = 0.1 % 以内)
-- 誤差を小さくするほど処理時間がかかる(デフォルトの 0.5 が通常は十分)
近似クエリを使うべき場面・使わない場面
近似クエリが適した場面
- リアルタイムダッシュボード:数秒以内のレスポンスが必要でわずかな誤差が許容できる
- 探索的データ分析(EDA):傾向をざっくり把握したい段階(後で正確値を取得)
- ログ・トラッキングデータ:数億件以上のアクセスログ・イベントデータのカーディナリティ
- パーセンタイルの傾向確認:レスポンスタイムの P95・P99 の推移確認(厳密値不要)
近似クエリを使ってはいけない場面
- 請求・会計処理:金額の合計・カウントは厳密な値が必要(APPROX_SUM は不向き)
- コンプライアンスレポート:監査目的のレポートには正確値が必要
- 小規模データ:数万件程度では COUNT(DISTINCT) も速く、近似の意味がない
- ロジックの分岐条件:「ユーザー数が 1000 人以上かどうか」で処理を分ける場合は正確値を使う
まとめ
- APPROX_COUNT_DISTINCT:Oracle 12.2 以降。COUNT(DISTINCT) の近似版。大規模データでのユニーク数集計が大幅に速い
- APPROX_COUNT_DISTINCT_DETAIL / AGG:中間集計(スケッチ)を BLOB で保存して後から再集計できる。マテリアライズドビュー的な活用が可能
- APPROX_PERCENTILE:PERCENTILE_DISC/CONT の近似版。GROUP BY との組み合わせで中央値・P95・P99 を高速計算できる
- APPROX_MEDIAN:Oracle 19c 以降。APPROX_PERCENTILE(0.5) と同等のシンタックスシュガー
- 誤差保証:APPROX_COUNT_DISTINCT はほぼ 2 % 以内。APPROX_PERCENTILE はランク誤差 0.5 % 以内がデフォルト
- 使い分け:ダッシュボード・EDA・大規模ログ分析には近似クエリ。請求・監査・ロジック分岐には正確クエリを使う
大規模データの集計パフォーマンス改善には、インメモリ列ストア(Oracle In-Memory)も有効です。Oracle In-Memory 完全ガイドを参照してください。マテリアライズドビューで集計結果を事前計算する方法は マテリアライズドビュー完全ガイドも参照してください。