2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BigQuery最適化: 相関サブクエリはスカラーサブクエリを配列サブクエリにするとフィルタされる

Last updated at Posted at 2025-12-22

この記事はBigQuery Advent Calendar 2025の記事です。

はじめに

手書きで単独のクエリを書くときは、不要なカラムをSQL中に書くことはありません。
しかし、BIツールからクエリを実行すると、BIツール側の条件に応じて実行されるクエリが変更されます。
この際、BigQueryでは明らかに不要なテーブル・カラムは取得しなくなります。

しかし、スカラー相関サブクエリでは、不要なテーブル・カラムへアクセスされていましたので、状況を調査して回避策を調べました。

BigQueryの不要テーブル・カラムの最適化について

BigQueryではクエリ上で明らかに不要なテーブル・カラムは取得しなくなります。またプッシュダウンの機能により、BIツール側の条件をソース側に波及させ、不要なカラム・テーブルの取得を回避することができます。

例えば、以下のようなクエリではhogeテーブルは明らかに不要なので取得することはありません。

    select
        if(true, 1, (select .. from hoge))

また、BigQueryではプライマリクエリを利用したフィルタの最適化が行われます。例えば以下のようなクエリでは、tbl2.idがprimary keyの場合、不要なtbl2は取得しません。

declare TARGET_GROUP default 'tbl1.hoge';

select
    case TARGET_GROUP
        'tbl1.hoge' then tbl1.hoge
        'tbl2.hoge' then tbl2.hoge
    end
from tbl1
left outer join tbl2 using(id)

BigQueryの相関サブクエリについて

BigQueryでは相関サブクエリを利用できます。しかし、RDBのように行ごとにサブクエリを実行することはなく、相関サブクエリはJOINに変換されます。

例えば以下のような相関サブクエリを考えます。

select
  (select group_name from grps where grps.group_id = tbl1.group_id) as grp,
  count(*) as cnt,
from tbl1
group by grp

この相関サブクエリは以下のようなJOINに変換されて実行されます。(実際には重複を省く処理などが入ります。

select
  grps.group_name as grp,
  count(*) as cnt,
from tbl1
left outer join grps on tbl1.group_id = grps.group_id
group by grp

スカラー相関サブクエリのCASE文でのフィルタについて

さて、クエリを実行して気づいたのですが、スカラー相関サブクエリをCASE文中で利用した場合、明らかに利用されないテーブルも読み込まれていました。なぜか最適化が行われず、JOINがされているようでした。

例えば以下のクエリではgrpsテーブルの読み込みは不要なはずですが、読み込まれていました。

declare TARGET_GROUP default 'all';

create temp table tbl1 as (
  select 1 as group_id, 'hoge' as text
  union all
  select 2 as group_id, 'fuga' as text
);

create temp table grps as (
  select 1 as group_id, 'grp1' as group_name
  union all
  select 2 as group_id, 'grp2' as group_name
);

select
  case TARGET_GROUP
    when "all" then "all"
    when "group" then (select group_name from grps where grps.group_id = tbl1.group_id)
  end as grp,
  count(*) as cnt,
from tbl1
group by grp

image.png

このクエリは、実質的には以下のように相関サブクエリがJOINに変換されて実行されているようです。

create temp table tbl1 as (
  select 1 as group_id, 'hoge' as text
  union all
  select 2 as group_id, 'fuga' as text
);

create temp table grps as (
  select 1 as group_id, 'grp1' as group_name
  union all
  select 2 as group_id, 'grp2' as group_name
);

select
  case "all"
    when "all" then "all"
    when "group" then grps.group_name
  end as grp,
  count(*) as cnt,
from tbl1
left outer join grps on tbl1.group_id = grps.group_id
group by grp

image.png

スカラー相関サブクエリを配列相関サブクエリにして最適化

そこで、スカラー相関サブクエリを以下のように配列相関サブクエリに変換すると、不要なテーブルへのアクセスがなくなりました。

select ARRAY(select ....)[SAFE_OFFSET(0)]
create temp table tbl1 as (
  select 1 as group_id, 'hoge' as text
  union all
  select 2 as group_id, 'fuga' as text
);

create temp table grps as (
  select 1 as group_id, 'grp1' as group_name
  union all
  select 2 as group_id, 'grp2' as group_name
);

select
  case "all"
    when "all" then "all"
    when "group" then grps.group_name
  end as grp,
  count(*) as cnt,
from tbl1
left outer join grps on tbl1.group_id = grps.group_id
group by grp

image.png

配列サブクエリのデメリット

配列サブクエリにしてOFFSETで取り出すと、結果が2つ以上帰ってきた場合のエラーチェックはなくなります。

まとめ

スカラー相関サブクエリを配列相関サブクエリにすることで、不要なテーブル・カラムの取得を回避して最適化できることを確認しました。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?