0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redshift クエリで冒頭に「値の集合」を定義して再利用する方法

Last updated at Posted at 2025-12-14

まとめ

Redshift クエリでは「WITH 句で『値の集合』を定義しておき、後段の IN 条件でそのまま再利用する」ということができません。どうしても先に条件判定用の値の集合を定義しておきたい場合は、 ARRAY() で定義しておき、複数行に展開して利用する」 方法があります。

モチベーション

RedShift クエリで冒頭の WITH 句に集計の設定値を寄せたいことがあると思います。このとき、「{りんご, みかん}の売り上げを集計する」のように「対象値の集合」としての設定値も冒頭に記述しておきたいこともあると思います。
しかし、RedShift には「そのまま IN 条件に渡せる値の集合」を予め定義しておく仕組みはありません。そのため、このようなケースでは ARRAY() で (SUPER 型として) 定義しておき、(SUPER 型のネスト解除構文で) 複数行に展開して利用する」 のが一つの方法です。

  • ARRAY() (SUPER 型) で値の集まりを定義しておくことはできますが、これはそのままでは「A 列の値がこの中に含まれるか」という判定はできません。
  • もし{りんご, みかん}を今後の集計でもずっと使うなら「果物マスタテーブル」を作成しておいた方がよいと思います。

冒頭に全部寄っていない例

対象期間の ('りんご', 'みかん') の売り上げだけを抽出したいとします。ただ、以下のクエリでは ('りんご', 'みかん') が冒頭に寄せられていません。

WITH conf AS (  -- 集計対象の定義
  SELECT
    20251201 AS lb,  -- 開始日
    20251210 AS ub   -- 終了日
),
data AS (  -- テストデータ
  SELECT
    v[0] AS date,
    v[1]::TEXT AS user_name,
    v[2]::TEXT AS item_name,
    v[3] AS quantity
  FROM (
    SELECT ARRAY(
      ARRAY(20251130, '太郎', 'りんご', 2000),
      ARRAY(20251201, '太郎', 'みかん', 1000),
      ARRAY(20251203, '次郎', 'りんご', 4000),
      ARRAY(20251210, '三郎', 'りんご', 2000),
      ARRAY(20251211, '三郎', 'みかん', 2000),
      ARRAY(20251212, '三郎', 'チョコ', 3000)) AS arr
  ) AS t, t.arr AS v
)
SELECT
  d.date,
  d.user_name,
  d.item_name,
  d.quantity
FROM data d
CROSS JOIN conf c
WHERE
  d.date BETWEEN c.lb AND c.ub
  AND d.item_name IN ('りんご', 'みかん')  -- 対象品目集合

実行結果は以下になり、対象期間の対象品目の売り上げは抽出できます。

date,user_name,item_name,quantity
20251201,太郎,みかん,1000
20251203,次郎,りんご,4000
20251210,三郎,りんご,2000

冒頭に全部寄せた例

以下のクエリでは ('りんご', 'みかん') も冒頭に寄せています。ただし、 ARRAY('りんご', 'みかん') のままでは判定に利用できないので、PartiQL 構文 で複数行に展開しています。

WITH conf AS (  -- 集計対象の定義
  SELECT
    20251201 AS lb,  -- 開始日
    20251210 AS ub,  -- 終了日
    ARRAY('りんご', 'みかん') AS arr_fruits  -- 対象品目集合
),
tbl_fruits AS (  -- 対象品目集合を展開
  SELECT v::TEXT AS item_name
  FROM conf c, c.arr_fruits v
),
data AS (  -- テストデータ
  SELECT
    v[0] AS date,
    v[1]::TEXT AS user_name,
    v[2]::TEXT AS item_name,
    v[3] AS quantity
  FROM (
    SELECT ARRAY(
      ARRAY(20251130, '太郎', 'りんご', 2000),
      ARRAY(20251201, '太郎', 'みかん', 1000),
      ARRAY(20251203, '次郎', 'りんご', 4000),
      ARRAY(20251210, '三郎', 'りんご', 2000),
      ARRAY(20251211, '三郎', 'みかん', 2000),
      ARRAY(20251212, '三郎', 'チョコ', 3000)) AS arr
  ) AS t, t.arr AS v
)
SELECT
  d.date,
  d.user_name,
  d.item_name,
  d.quantity
FROM data d
CROSS JOIN conf c
JOIN tbl_fruits f ON d.item_name = f.item_name
WHERE d.date BETWEEN c.lb AND c.ub

このクエリも実行結果は先と同じになります。

date,user_name,item_name,quantity
20251201,太郎,みかん,1000
20251203,次郎,りんご,4000
20251210,三郎,りんご,2000

参考文献

上記の Stack Overflow 記事は「RedShift クエリで VALUES() によって複数行を定義できないのか」というものです。RedShift では INSERT 時に VALUES() によって複数行を挿入することはできますが(参考)、クエリ内で VALUES() によって複数行を定義することはできません。

そこで代替手段として 「1 行ずつ定義して UNION ALL で複数行にする」か「ARRAY() を定義して展開して複数行にする」方法が示されています。

この記事は ARRAY() 方式を採用していることになりますが、もしコンフィグ用 WITH 句が複数にまたがってもよいとか、設定値が{りんご, みかん}しかないとかの場合は、以下の UNION ALL 式の記述も採用できると思います。

WITH conf AS (  -- 集計対象の定義
  SELECT
    20251201 AS lb,  -- 開始日
    20251210 AS ub   -- 終了日
),
tbl_fruits AS (  -- くだものテーブル
  SELECT 'りんご' AS item_name UNION ALL
  SELECT 'みかん' AS item_name
),

ただ、先の Stack Overflow 記事には「UNION ALL 式ではクエリにオーバーヘッドがある」と書いている回答者がいます。実際 UNION ALL 式の方がクエリプランが肥大化しますが、それでクエリがどれほどオーバーヘッドするのかは私が詳しくないためよくわかっていません。ご存じの方がいたら教えてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?