Hive データ処理における SQL パターンと設定最適化

ユーザー別アクションの上位抽出

ウィンドウ関数を用いて、各ユーザーごとに最も長時間視聴したコンテンツを特定するクエリ例です。サブクエリ内で ROW_NUMBER を使用し、パーティションごとに順位付けを行います。

SELECT user_id, content_id
FROM (
    SELECT 
        user_id,
        content_id,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY total_duration DESC) AS rank
    FROM (
        SELECT user_id, content_id, SUM(view_duration) AS total_duration
        FROM access_logs
        GROUP BY user_id, content_id
    ) aggregated
) ranked
WHERE rank = 1;

週次集計のための日付計算

Hive において、基準日からの経過日数を計算し、週頭(月曜日)の日付を導出する方法です。基準日を変更することで、任意の週の始点を算出できます。

-- 指定日の曜日を計算 (0=月曜日〜6=日曜日)
SELECT pmod(datediff('2023-10-01', '2020-01-01'), 7);

-- 当該週の月曜日
SELECT date_sub('2023-10-01', pmod(datediff('2023-10-01', '2020-01-01'), 7));

-- 前週の月曜日
SELECT date_sub('2023-10-01', pmod(datediff('2023-10-01', '2020-01-01'), 7) + 7);

スキーマ変更操作 (DDL)

既存テーブルの列名、データ型、および列の順序を変更する ALTER 文の構文です。列の位置を最初にする FIRST や、特定の列の後に配置する AFTER オプションが利用可能です。

-- 列名とタイプの変更
ALTER TABLE customer_master CHANGE COLUMN old_phone new_phone STRING;

-- 列を先頭に移動
ALTER TABLE customer_master CHANGE COLUMN new_phone new_phone STRING FIRST;

-- 特定の列の後に移動
ALTER TABLE customer_master CHANGE COLUMN address address STRING AFTER new_phone;

-- 新規列の追加(末尾に追加される)
ALTER TABLE customer_master ADD COLUMNS (membership_level INT);

-- テーブル名の変更
ALTER TABLE customer_master RENAME TO customer_profile;

パーティション管理

パーティションの追加、位置変更、削除操作です。HDFS 上のディレクトリ構造に対応するため、_location を指定することでデータ格納先を制御できます。

-- パーティション追加
ALTER TABLE event_logs ADD PARTITION (event_date='20231001');

-- 場所を指定してパーティション追加
ALTER TABLE event_logs ADD PARTITION (event_date='20231002') 
LOCATION '/data/events/20231002';

-- パーティション一覧の確認
SHOW PARTITIONS event_logs;

-- パーティション名の変更(日付変更など)
ALTER TABLE event_logs PARTITION (event_date='20231001') 
RENAME TO PARTITION (event_date='20231003');

-- 保存場所の変更
ALTER TABLE event_logs PARTITION (event_date='20231002') 
SET LOCATION '/data/events/moved/20231002';

-- パーティションの削除
ALTER TABLE event_logs DROP IF EXISTS PARTITION (event_date='20231001');

JSON データの解析

文字列形式の JSON データから特定のフィールドを抽出するには、get_json_object または json_tuple を使用します。配列形式の JSON を行展開するには、explode 関数と正規表現を組み合わせます。

-- 単一フィールドの抽出
SELECT get_json_object('{"product_id":"P100","category":"Electronics"}', '$.product_id');

-- 複数フィールドの同時抽出
SELECT json_tuple('{"product_id":"P100","category":"Electronics"}', 'product_id', 'category');

-- JSON 配列の展開処理
SELECT json_tuple(json_str, 'product_id', 'category') 
FROM (
    SELECT explode(
        split(
            regexp_replace(
                regexp_replace(
                    '[{"product_id":"P100"},{"product_id":"P200"}]', 
                    '\\},\\{', '\\};\\{'
                ), 
                '\\[|\\]', ''
            ), 
            '\\;'
        )
    ) AS json_str
) exploded_data;

上記の正規表現処理は、JSON 配列の区切り文字を置換し、配列括弧を除去してから split 関数で行ごとに分解しています。

演算子の優先順位と NULL 値の扱い

論理演算子の優先順位は NOT > AND > OR です。WHERE 句で OR を使用する場合、条件が独立して評価される点に注意が必要です。また、不等号演算子(<> や !=)は NULL 値を結果から除外するため、NULL を含める場合は IS NULL を明示的に記述します。

-- NULL 値を保持するための条件記述
WHERE (status_code <> 'ERROR' OR status_code IS NULL);

ソートと分散の仕組み

Hive にはデータ並べ替えに関する複数の句があり、それぞれ Reduce タスクの挙動が異なります。

  • ORDER BY: 全局ソート。すべてのデータが単一の Reduce タスクに集められるため、データ量が多い場合は注意が必要です。厳密モードでは LIMIT の指定が必須となります。
  • SORT BY: 局所ソート。各 Reduce タスク内でソートが行われます。全体としての順序は保証されませんが、処理効率は高いです。
  • DISTRIBUTE BY: 特定の列値を同じ Reduce タスクに送信するためのハッシュ分散制御を行います。SORT BY と組み合わせて使用されることが多いです。
  • CLUSTER BY: DISTRIBUTE BY と SORT BY を同じ列に対して指定した場合の簡略記法です。
-- 分散とソートの組み合わせ
SELECT user_id, amount, transaction_date 
FROM transactions 
DISTRIBUTE BY user_id 
SORT BY user_id;

-- CLUSTER BY による簡略化
SELECT user_id, amount, transaction_date 
FROM transactions 
CLUSTER BY user_id;

内部表と外部表の差異

EXTERNAL キーワードの有無により、表の管理方式が異なります。内部表(Managed Table)は Hive がデータ lifecycle を管理し、DROP 時にデータごと削除されます。外部表(External Table)はメタデータのみが削除され、HDFS 上の実データは残存します。

ストレージ形式については、行ストアは 1 ブロックに複数行を格納するのに対し、列ストアは列ごとにデータを格納し、分析クエリでの I/O 効率を向上させます。

静的および動的パーティション

パーティション値を挿入時に明示的に指定するのが静的パーティションです。一方、クエリ結果に基づいて自動的にパーティションを決定するのが動的パーティションです。大量のパーティションを扱う場合、動的パーティションが効率的ですが、設定変更が必要です。

-- 動的パーティションの有効化
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;

-- 動的パーティションへのデータ挿入
INSERT INTO TABLE staff_records PARTITION (department_id)
SELECT emp_id, emp_name, role, manager_id, hire_date, salary, department_id
FROM source_employee_data;

INSERT 文の SELECT 句の最後の列が、PARTITION 句で指定した列に対応している必要があります。これにより、department_id の値ごとに自動的にディレクトリが作成されます。

タグ: apache-hive sql-window-functions etl-pipeline data-warehouse hive-ql

6月29日 23:11 投稿