Omniで動的に年次・月次などの集計テーブルを参照してスキャンコストを削減できる「Aggregate Awareness」を試してみた

Omniで動的に年次・月次などの集計テーブルを参照してスキャンコストを削減できる「Aggregate Awareness」を試してみた

2025.11.24

さがらです。

BIツールを用いた分析を行う際、最も粒度が細かいテーブルを都度参照するとレコード量が多いためスキャンコストがかかってしまうことがあります。

そんな問題に対して、Omniは動的に年次・月次などの集計テーブルを参照してスキャンコストを削減できる「Aggregate Awareness」を備えています。

https://docs.omni.co/docs/querying-and-sql/caching#aggregate-awareness

https://community.omni.co/t/i-have-tables-in-my-dwh-that-have-different-levels-of-aggregation-how-can-i-use-aggregate-awareness-to-query-dynamically/123

この機能を試してみたので、試した内容について本記事でまとめてみます。

やること

まず前提として、粒度が細かい注文レベルの情報を保持するordersテーブルがあり、Omniでは以下のようにorders.viewを定義していたとします。

このorders.viewに対して、年次・月次で集計したテーブルを別途用意し、Aggregate Awarenessがどのように動作するかを確かめてみます。

# Reference this view as omni_dbt_mart__orders
schema_label: ""
description: 注文概要データマート 。顧客の最初の注文かどうか、食べ物と飲み物の内訳など、各注文の重要な詳細を提供します。注文ごとに 1 行。

schema: omni_dbt_mart
table_name: ORDERS

dimensions:
  order_id:
    sql: '"ORDER_ID"'
    format: ID
    #This description was pulled from dbt.
    description: プライマリキー

  location_id:
    sql: '"LOCATION_ID"'
    format: ID

  customer_id:
    sql: '"CUSTOMER_ID"'
    format: ID
    #This description was pulled from dbt.
    description: 注文顧客と紐づける外部キー

  subtotal_cents:
    sql: '"SUBTOTAL_CENTS"'

  tax_paid_cents:
    sql: '"TAX_PAID_CENTS"'

  order_total_cents:
    sql: '"ORDER_TOTAL_CENTS"'

  subtotal:
    sql: '"SUBTOTAL"'

  tax_paid:
    sql: '"TAX_PAID"'

  order_total:
    sql: '"ORDER_TOTAL"'
    #This description was pulled from dbt.
    description: 税込みの注文総額 (USD)

  order_cost:
    sql: '"ORDER_COST"'
    #This description was pulled from dbt.
    description: 注文を履行するための供給費の合計

  order_items_subtotal:
    sql: '"ORDER_ITEMS_SUBTOTAL"'

  count_food_items:
    sql: '"COUNT_FOOD_ITEMS"'

  count_drink_items:
    sql: '"COUNT_DRINK_ITEMS"'

  count_order_items:
    sql: '"COUNT_ORDER_ITEMS"'

  is_food_order:
    sql: '"IS_FOOD_ORDER"'
    #This description was pulled from dbt.
    description: この注文に食品が含まれているかどうかを示すブール値

  is_drink_order:
    sql: '"IS_DRINK_ORDER"'
    #This description was pulled from dbt.
    description: この注文にドリンクアイテムが含まれているかどうかを示すブール値

  customer_order_number:
    sql: '"CUSTOMER_ORDER_NUMBER"'

  purchased_at:
    sql: '"PURCHASED_AT"'
    #This description was pulled from dbt.
    description: 注文が行われたタイムスタンプ

measures:
  count:
    aggregate_type: count

  order_total_sum:
    sql: ${omni_dbt_mart__orders.order_total}
    aggregate_type: sum

  orders_count_new_customer:
    sql: |-
      COUNT(
        CASE
          WHEN customer_order_number = 1 THEN 1
          ELSE 0
        END
      )
    label: Orders Count New Customer

  # --- 以下はaggregate awareness検証用---
  total_revenue:
    sql: ${omni_dbt_mart__orders.order_total}
    aggregate_type: sum

  total_cost:
    sql: ${omni_dbt_mart__orders.order_cost}
    aggregate_type: sum

  total_subtotal:
    sql: ${omni_dbt_mart__orders.order_items_subtotal}
    aggregate_type: sum

  total_tax:
    sql: ${omni_dbt_mart__orders.tax_paid}
    aggregate_type: sum

  total_profit:
    sql: ${omni_dbt_mart__orders.order_total} - ${omni_dbt_mart__orders.order_cost}
    aggregate_type: sum

  total_items_sold:
    sql: ${omni_dbt_mart__orders.count_order_items}
    aggregate_type: sum

  total_food_items:
    sql: ${omni_dbt_mart__orders.count_food_items}
    aggregate_type: sum

  total_drink_items:
    sql: ${omni_dbt_mart__orders.count_drink_items}
    aggregate_type: sum

  count_orders_with_food:
    aggregate_type: count
    filters:
      is_food_order:
        is: true

  count_orders_with_drink:
    aggregate_type: count
    filters:
      is_drink_order:
        is: true

#The info below was pulled from your dbt repository and is read-only.
dbt:
  name: orders
  target_schema: PROD
  description: 注文概要データマート 。顧客の最初の注文かどうか、食べ物と飲み物の内訳など、各注文の重要な詳細を提供します。注文ごとに 1 行。
  config:
    schema: mart
    materialized: table
  code: |-
    with

    orders as (

        select * from {{ ref('stg_orders') }}

    ),

    order_items as (

        select * from {{ ref('order_items') }}

    ),

    order_items_summary as (

        select
            order_id,

            sum(supply_cost) as order_cost,
            sum(product_price) as order_items_subtotal,
            count(order_item_id) as count_order_items,
            sum(
                case
                    when is_food_item then 1
                    else 0
                end
            ) as count_food_items,
            sum(
                case
                    when is_drink_item then 1
                    else 0
                end
            ) as count_drink_items

        from order_items

        group by 1

    ),

    compute_booleans as (

        select
            orders.*,

            order_items_summary.order_cost,
            order_items_summary.order_items_subtotal,
            order_items_summary.count_food_items,
            order_items_summary.count_drink_items,
            order_items_summary.count_order_items,
            order_items_summary.count_food_items > 0 as is_food_order,
            order_items_summary.count_drink_items > 0 as is_drink_order

        from orders

        left join
            order_items_summary
            on orders.order_id = order_items_summary.order_id

    ),

    customer_order_count as (

        select
            *,

            row_number() over (
                partition by customer_id
                order by purchased_at asc 
                -- order by ordered_at asc
            ) as customer_order_number

        from compute_booleans

    )

    select * from customer_order_count
  referenced_by: [ customers, orders_yearly, orders_monthly, product_total_sales ]

dbtで年次・月次の集計テーブルの作成

まず、dbtを使って年次・月次の集計テーブルを作成します。下図のように作成しておきました。(dbtで直にmodelを作成せずとも、OmniのWorkbook上で選択したフィールドに基づいた集計結果をdbtのModelとしてプッシュできる機能を用いる方法もあります。)

  • 年次

2025-11-24_07h06_12

  • 月次

2025-11-24_07h06_41

このテーブルを使用するDWHの本番環境にもビルド後、OmniのShared ModelでRefresh schemaを押します。

2025-11-24_07h08_28

これで、Omni側にも年次・月次の集計テーブルを参照するviewが作られました。(下図は月次のテーブルを元に作られたview)

2025-11-24_07h09_43

年次・月次のviewに対してmaterialized_queryを追加

次に、Omniに作られた年次・月次のviewに対してmaterialized_queryを追加します。materialized_queryはAggregate Awareness用のオブジェクトで、集計前のviewのフィールドと比較して集計テーブルのどのカラムがマッチするのかを定義します。

https://docs.omni.co/docs/modeling/views#materialized_query-aggregate-awareness

重要なポイントはfields:の定義順序で、DWH上の集計テーブルのカラムの順序に合わせて、集計前のviewのフィールドのうちどのフィールドがマッチするのか定義する必要があります。また、日付フィールドは[month][year]のように書いて、どの粒度の日付フィールドと集計テーブルの日付カラムがマッチするのか定義する必要があるためご注意ください。

今回は下記のように定義しました。(スキーマ名はDynamic Schema検証の影響でvirtual schemaになっています。)

  • 年次 ※以下の内容を年次集計テーブルから作られたOmniのviewの最下部に追記
materialized_query:
  fields:
    [
      "omni_dbt_mart__orders.purchased_at[year]",
      omni_dbt_mart__orders.total_revenue,
      omni_dbt_mart__orders.total_cost,
      omni_dbt_mart__orders.total_subtotal,
      omni_dbt_mart__orders.total_tax,
      omni_dbt_mart__orders.total_profit,
      omni_dbt_mart__orders.count,
      omni_dbt_mart__orders.total_items_sold,
      omni_dbt_mart__orders.total_food_items,
      omni_dbt_mart__orders.total_drink_items,
      omni_dbt_mart__orders.count_orders_with_food,
      omni_dbt_mart__orders.count_orders_with_drink
    ]
  base_view: omni_dbt_mart__orders
  • 月次 ※以下の内容を月次集計テーブルから作られたOmniのviewの最下部に追記
materialized_query:
  fields:
    [
      "omni_dbt_mart__orders.purchased_at[month]",
      omni_dbt_mart__orders.total_revenue,
      omni_dbt_mart__orders.total_cost,
      omni_dbt_mart__orders.total_subtotal,
      omni_dbt_mart__orders.total_tax,
      omni_dbt_mart__orders.total_profit,
      omni_dbt_mart__orders.count,
      omni_dbt_mart__orders.total_items_sold,
      omni_dbt_mart__orders.total_food_items,
      omni_dbt_mart__orders.total_drink_items,
      omni_dbt_mart__orders.count_orders_with_food,
      omni_dbt_mart__orders.count_orders_with_drink
    ]
  base_view: omni_dbt_mart__orders

これらの内容を追記後、下図のようになります。(下図は月次のテーブルを元に作られたview)

2025-11-24_07h39_30

Aggregate Awarenessの動作確認

これで設定は完了のため、Aggregate Awarenessがどのように動作するかを確認してみます!(参考までに確認手順としては、今回はブランチでShared Modelを編集しているため、IDEの右上からExploreで動作確認します。)

年次の集計テーブルに対する動作確認

まず、年次の集計テーブルが参照されているか確認するため、Yearのディメンションとメジャーを1つ選択してみます。この状態だとAggregate Awarenessが効いていないように見えます。

2025-11-24_07h45_11

実際にDWHに発行されているクエリを確認するため、Inspectorを表示します。

2025-11-24_07h46_17

InspectorからDWHへ発行されたクエリを確認すると、FROM句の後が"MART"."ORDERS_YEARLY"と年次の集計テーブルを参照していることがわかります!

2025-11-24_07h47_45

月次の集計テーブルに対する動作確認

続いて、Monthのディメンションを用いてクエリを発行してみます。

2025-11-24_07h52_33

InspectorからDWHへ発行されたクエリを確認すると、FROM句の後が"MART"."ORDERS_MONTHLY"と月次の集計テーブルを参照していることがわかります!

2025-11-24_07h53_08

注意点:キャッシュが効いている場合

注意点として、Omniのキャッシュが効いている場合はAggregate Awarenessは機能せず、キャッシュされた結果からクエリするようになっています。

※今回の検証で私も初めて知ったのですが、キャッシュされた結果から一部フィールドを選択、あるいはWHERE句で絞り込んで結果を返せる場合でも、キャッシュが効きます!これは凄い…(Omniのキャッシュに関する公式Doc

参考までに、キャッシュが効いている場合のInspectorの画像を以下に貼っておきます。

2025-11-24_07h59_25

2025-11-24_08h01_48

最後に

Omniで動的に年次・月次などの集計テーブルを参照してスキャンコストを削減できる「Aggregate Awareness」を試してみました。

レコード量が多いデータに対する分析を行う際にとても有用に働く機能だと思いますので、ぜひご活用ください!

この記事をシェアする

FacebookHatena blogX

関連記事