分析メモ

【BigQuery】複数のテーブル&列からURLを抽出する

BigQueryの複数のテーブル&複数の列からデータを抽出するのに、UNPIVOT関数と配列関数が役に立ったので記録しておきます。

状況

BigQueryからこんなデータが抽出できないか、との依頼がありました。

すべてのブログに含まれる文字データから、URLを抽出してほしい。
URLごとに、誰の・どのデータに含まれていたのかも分かるようにして。

ブログパーツは、記事やパーツなどの属性ごとに異なるテーブルに格納されていました。
今回は簡単のため、本文とライター別パーツが2テーブルに格納されているものとします。

列名データ型制約null
idint主キー不可
writer_idint不可
document_idint不可
titlestr不可
bodystr
記事テーブル (blog_article_tbl)
列名データ型制約null
idint主キー不可
writer_idint不可
position_typeint不可
textstr
link_urlstr
共通パーツテーブル (blog_component_tbl)

上のようなテーブルを用いて、以下のようなテーブルを作るのが目標です。

writer_idtable_nameidcolumn_nameurl
001article101titlehttp://hoge.com
001component30texthttps://fuga.co.jp/
002article26bodyhttps://piyo.net/piyopiyo/
002article26bodyhttp://hogehoge.jp/
出力イメージ

課題と対策

この抽出にあたり、おおまかには下記2つの課題がありました。

  1. 複数テーブルの複数列に対して、一様なURL抽出処理が必要。
  2. 同じデータに含まれるURLは一つとは限らない。1データから複数のURLを抽出する必要がある。

これらの課題を解決するために、以下のような方法を取ることにしました。

  1. 一様な処理: UNPIVOT関数で、すべての対象列を一列にまとめる。
  2. 複数URLの抽出: REGEXP_EXTRACT_ALL関数で、1データから複数のURLを配列として抽出。UNNEST関数を使って配列を分解し、1URL1行のテーブルを出力する。

用いたSQL

実際に用いたSQLは下記のようなものです。

declare url_regex default r'https?://(?:[\w-]+\.)+[\w-]+(?:/[\w./?%&=]*)?';
-- URLを捕捉するための正規表現を予め宣言。

-- 1. テーブルごとの捜索対象列を全て縦に並べる。その後、各テーブルをUNION ALLで結合。
with melt_tbl as (
  select
    member_id
    ,'article' as table_name
    ,id
    ,column_name
    ,strings
  from 
    blog_article_tbl
    ,unpivot exclude nulls (
      strings -- 捜索対象データが格納される列名
      for column_name in (title, body) -- 捜索対象データが含まれる列名を、新規テーブルのcolumn_name列に格納
    )
  union all
  select
    member_id
    ,'component' as table_name
    ,id
    ,column_name
    ,strings
  from 
    blog_component_tbl
    ,unpivot exclude nulls (
      strings
      for column_name in (text, link_url)
    )
)
-- 2. melt_tblの行ごとに複数のURLを抽出。
,extract_tbl as (
  select
    member_id
    ,table_name
    ,id
    ,column_name
    ,regexp_extract_all(strings, url_regex) as urls
  from
    melt_tbl
)
-- 3. extract_tblの配列を分解し、1行に1URLが含まれるテーブルを出力。
select
  member_id
  ,table_name
  ,id
  ,column_name
  ,url
from
  extract_tbl
  ,unnest(urls) as url -- 配列をunnest
;

補足

モバイルバージョンを終了