BigQuery Migration Service の SQL 変換機能が一般提供になりました

Redshift や Teradata の SQL を BigQuery へ自動変換する機能が一般提供になりました

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ウィスキー、シガー、パイプをこよなく愛する大栗です。

BigQuery への移行をサポートする BigQuery Migration Service が昨年 10 月に SQL を変換する機能がプレビューとして公開されていましたが、このたび一般提供となりました。これと主に SQL オブジェクトの名称マッピングやメタデータを抽出するツールもプレビューとして公開されたのでレポートしています。

BigQuery Migration Service

BigQuery Migration Service(BQMS) は既存のデータウェアハウスを BigQuery へ移行するためのプロセスをサポートする機能を備えており、無料で利用できるツールセットです。Teradata のアセスメントと計画、10 以上の方言に対応した SQL 変換、データ転送、データ検証などの移行の各フェーズを支援するツールとなっています。

BigQuery への移行が容易に

今回一般提供となったのは SQL の変換を行う以下の機能です。

  • Batch SQL translator: Cloud Storage 上の異なる SQL 方言スクリプトを BigQuery が使用している標準 SQL へバッチ変換します
  • Interactive SQL translator: Cloud Console 上で異なる SQL 方言を BigQuery が使用している標準 SQL へ変換します

Batch SQL translator

変換元となる SQL 方言は以下がサポートされています。変換はベストエフォートで行われ、成功するかは SQL 文の独自性と複雑性によって異なります。一部のスクリプトは手動で変換する必要がある場合があります。

  • Amazon Redshift SQL
  • Teradata SQL (SPLを除く)
  • Apache HiveQL プレビュー
  • Apache Spark SQL プレビュー
  • Azure Synapse T-SQL プレビュー
  • Basic Teradata Query (BTEQ) プレビュー
  • IBM Netezza SQL/NZPLSQL プレビュー
  • Oracle SQL, PL/SQL, Exadata プレビュー
  • Snowflake SQL プレビュー
  • Teradata SPL プレビュー
  • Vertica SQL プレビュー

dwh-migration-dumperツールを使用してメタデータ情報を生成することができ、メタデータ情報によって変換の精度を向上させることもできます。dwh-migration-dumperツールは Apache 2 License となっており、Windows、macOS、Linux で実行できます。実行には Java 8 以降が必要となります。

また、バッチ変換中にオブジェクトの名称を変換する事もできます。

Interactive SQL translator

変換元となる SQL 方言は以下がサポートされています。

  • Amazon Redshift SQL
  • Teradata SQL (SPLを除く)
  • Apache HiveQL プレビュー
  • Apache Spark SQL プレビュー
  • IBM Netezza SQL/NZPLSQL プレビュー
  • Oracle SQL, PL/SQL, Exadata プレビュー
  • Snowflake SQL プレビュー
  • Teradata SPL プレビュー

Interactive SQL translator の制限事項として、入力したクエリのスキーマ情報を持っていません。正確な翻訳を行うためにはクエリで使用するテーブルの DDL をクエリの前に入力します。例えば以下のクエリの最初の 2 行のように DDL を記述します。

create table schema1.table1 (id int, field1 int, field2 varchar(16));
create table schema1.table2 (id int, field1 varchar(30), field2 date);

select table1.field1, table2.field1
from table1, table2
where table1.id = table2.id;

やってみる

前提として、変換元は Amazon Redshift とし、対象データはTICKITを使用します。Redshift は事前に起動しておきます。起動時にサンプルデータを入れておくとそのまま検証を行えます。そうでない場合はドキュメントを元にデータセットをロードしてください。

クライアントは PostgreSQL クライアントと Google Cloud SDK がインストール済みとします。

変換元 SQL ファイルの準備

作業用ディレクトリを作成します。

$ mkdir tickitquery

TICKIT のテーブル定義をエクスポートします。

$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE CATEGORY ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE DATE ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE EVENT ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE VENUE ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE USERS ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE LISTING ;' >> ./tickitquery/Query.sql
$ PGPASSWORD=<PASSWORD> psql -h <REDSHIFT_HOST_NAME> \
  -U awsuser -d dev -p 5439 -t -A \
  -c 'SHOW TABLE SALES ;' >> ./tickitquery/Query.sql

作成した./tickitquery/Query.sqlに以下の 5 個のクエリを追記します。

Select1

select sellerid, username, (firstname ||' '|| lastname) as name,
city, sum(qtysold)
from sales, date, users
where sales.sellerid = users.userid
and sales.dateid = date.dateid
and year = 2008
and city = 'San Diego'
group by sellerid, username, name, city
order by 5 desc
limit 5;

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_sampledb.html

Select2

SELECT catgroup, SUM(qtysold) AS sold
FROM category c, event e, sales s
WHERE c.catid = e.catid AND e.eventid = s.eventid
GROUP BY 1;

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/materialized-view-auto-rewrite.html

Select3

select listagg(distinct sellerid, ', ') within group (order by sellerid) from sales
where eventid = 4337;

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_LISTAGG.html

Select4

select catgroup1, sold, unsold
from
(select catgroup, sum(qtysold) as sold
from category c, event e, sales s
where c.catid = e.catid and e.eventid = s.eventid
group by catgroup) as a(catgroup1, sold)
join
(select catgroup, sum(numtickets)-sum(qtysold) as unsold
from category c, event e, sales s, listing l
where c.catid = e.catid and e.eventid = s.eventid
and s.listid = l.listid
group by catgroup) as b(catgroup2, unsold)

on a.catgroup1 = b.catgroup2
order by 1;

https://docs.aws.amazon.com/redshift/latest/dg/r_Join_examples.html

Select5

with venuecopy as (select * from venue)
select * from venuecopy order by 1 limit 10;

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_WITH_clause.html

tickitqueryディレクトリのファイルを Google Cloud Storage へアップロードします。

$ gsutil cp ./tickitquery/Query.sql gs://<GCS_BUCKET_NAME>

Batch SQL translator の実行

Cloud Console で BigQuerySQL translationを開きます。初めて使用する場合にはBigQuery Migration APIが有効になっていないため、ENABLEをクリックして有効化します。

BigQuery Migration API の有効化

START TRANSLATIONをクリックします。

BigQuery SQL translation

Display nameには変換ジョブの名称、Data locationはデータの配置先、Source dialectは変換元のデータベースでここではRedshiftTarget dialectは変換先で現状はBigQueryのみ選択できます。入力後にNEXTをクリックします。

Source locationにアップロードしたファイルのあるフォルダを指定してNEXTをクリックします。

Target locationに変換したファイルの出力先フォルダを指定してNEXTをクリックします。

最後にオプション設定がありますが、ここでは何も設定せずにCREATEをクリックします。

変換が完了するまで少し待ちます。

変換が完了したら Target path をクリックして対象のパスの Cloud Storage へ遷移します。

Cloud Storage に以下のようなファイルが出力されています。

  • query1.sql : 変換後の SQL ファイル
  • batch_translation_report.csv : CSV 形式のバッチ変換を行った概要レポート(変換エラーがある場合に記載されます)
  • consumed_name_map.json : JSON 形式の使用した出力名のマッピング

Batch SQL translator の結果確認

各々の内容を確認してみます。

CATEGORY テーブル

CREATE TABLE 文の内容は以下のような違いがあります。数値型は BigQuery ではINT64になっています。BigQuery にもSMALLINT型はありますが、INT64のエイリアスなので一般的な整数型はINT64型になるようです。文字列は長さの指定がなくなりSTRING型に変換されています。Redshift と BigQuery ではアーキテクチャに違いがあり、データの分散を意識しないためオプションはなくなります。ソートキーはクラスタリングの列指定に変わっています。BigQuery では圧縮方法の選択が無いため Redshift の列圧縮はなくなります。

  • smallint → INT64
  • character varying(10) → STRING
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE lzo → 無し

CATEGORYテーブル(変換前)

CREATE TABLE public.category (
    catid smallint NOT NULL ENCODE raw distkey,
    catgroup character varying(10) ENCODE lzo,
    catname character varying(10) ENCODE lzo,
    catdesc character varying(50) ENCODE lzo
)
DISTSTYLE KEY
SORTKEY ( catid );

CATEGORYテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.category
(
  catid INT64 NOT NULL,
  catgroup STRING,
  catname STRING,
  catdesc STRING
)
CLUSTER BY catid;

DATE テーブル

CREATE TABLE 文の内容は以下のような違いがあります。CATEGORY テーブルと同様ですが、character型はSTRING型に変換されます。列の圧縮や非圧縮の指定はなくなります。

  • smallint → INT64
  • character → STRING
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE lzo/az64/raw → 無し

DATEテーブル(変換前)

CREATE TABLE public.date (
    dateid smallint NOT NULL ENCODE raw distkey,
    caldate date NOT NULL ENCODE az64,
    day character(3) NOT NULL ENCODE lzo,
    week smallint NOT NULL ENCODE az64,
    month character(5) NOT NULL ENCODE lzo,
    qtr character(5) NOT NULL ENCODE lzo,
    year smallint NOT NULL ENCODE az64,
    holiday boolean DEFAULT false ENCODE raw
)
DISTSTYLE KEY
SORTKEY ( dateid );

DATEテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.date
(
  dateid INT64 NOT NULL,
  caldate DATE NOT NULL,
  day STRING NOT NULL,
  week INT64 NOT NULL,
  month STRING NOT NULL,
  qtr STRING NOT NULL,
  year INT64 NOT NULL,
  holiday BOOL
)
CLUSTER BY dateid;

EVENT テーブル

CREATE TABLE 文の内容は以下のような違いがあります。他のテーブルと同様ですが、character型はSTRING型に変換されます。timestamp without time zoneDATETIMEに変換されます。ソートキーだけでなく分散キーもクラスタリングの列指定となっています。

  • smallint/integer → INT64
  • character → STRING
  • timestamp without time zone → DATETIME
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE lzo/az64 → 無し

EVENTテーブル(変換前)

CREATE TABLE public.event (
    eventid integer NOT NULL ENCODE az64 distkey,
    venueid smallint NOT NULL ENCODE az64,
    catid smallint NOT NULL ENCODE az64,
    dateid smallint NOT NULL ENCODE raw,
    eventname character varying(200) ENCODE lzo,
    starttime timestamp without time zone ENCODE az64
)
DISTSTYLE KEY
SORTKEY ( dateid );

EVENTテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.event
(
  eventid INT64 NOT NULL,
  venueid INT64 NOT NULL,
  catid INT64 NOT NULL,
  dateid INT64 NOT NULL,
  eventname STRING,
  starttime DATETIME
)
CLUSTER BY eventid, dateid;

VENUE テーブル

CREATE TABLE 文の内容は以下のような違いがあります。他のテーブルと同様の変換となります。

  • smallint/integer → INT64
  • character → STRING
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE lzo/az64 → 無し

VENUEテーブル(変換前)

CREATE TABLE public.venue (
    venueid smallint NOT NULL ENCODE raw distkey,
    venuename character varying(100) ENCODE lzo,
    venuecity character varying(30) ENCODE lzo,
    venuestate character(2) ENCODE lzo,
    venueseats integer ENCODE az64
)
DISTSTYLE KEY
SORTKEY ( venueid );

VENUEテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.venue
(
  venueid INT64 NOT NULL,
  venuename STRING,
  venuecity STRING,
  venuestate STRING,
  venueseats INT64
)
CLUSTER BY venueid;

USERS テーブル

CREATE TABLE 文の内容は以下のような違いがあります。他のテーブルと同様の変換となりますが、boolean型はBOOL型に変換されます。

  • integer → INT64
  • character → STRING
  • boolean → BOOL
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE lzo/az64 → 無し

USERSテーブル(変換前)

CREATE TABLE public.users (
    userid integer NOT NULL ENCODE raw distkey,
    username character(8) ENCODE lzo,
    firstname character varying(30) ENCODE lzo,
    lastname character varying(30) ENCODE lzo,
    city character varying(30) ENCODE lzo,
    state character(2) ENCODE lzo,
    email character varying(100) ENCODE lzo,
    phone character(14) ENCODE lzo,
    likesports boolean ENCODE raw,
    liketheatre boolean ENCODE raw,
    likeconcerts boolean ENCODE raw,
    likejazz boolean ENCODE raw,
    likeclassical boolean ENCODE raw,
    likeopera boolean ENCODE raw,
    likerock boolean ENCODE raw,
    likevegas boolean ENCODE raw,
    likebroadway boolean ENCODE raw,
    likemusicals boolean ENCODE raw
)
DISTSTYLE KEY
SORTKEY ( userid );

USERSテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.users
(
  userid INT64 NOT NULL,
  username STRING,
  firstname STRING,
  lastname STRING,
  city STRING,
  state STRING,
  email STRING,
  phone STRING,
  likesports BOOL,
  liketheatre BOOL,
  likeconcerts BOOL,
  likejazz BOOL,
  likeclassical BOOL,
  likeopera BOOL,
  likerock BOOL,
  likevegas BOOL,
  likebroadway BOOL,
  likemusicals BOOL
)
CLUSTER BY userid;

LISTING テーブル

CREATE TABLE 文の内容は以下のような違いがあります。他のテーブルと同様の変換となりますが、numeric(8,2)型がNUMERIC型に変換されます。Redshift では(8,2)の精度が定義されていましたが、変換後は精度が定義されていません。

  • integer/smallint → INT64
  • numeric(8,2) → NUMERIC
  • timestamp without time zone → DATETIME
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE az64/raw → 無し

LISTINGテーブル(変換前)

CREATE TABLE public.listing (
    listid integer NOT NULL ENCODE az64 distkey,
    sellerid integer NOT NULL ENCODE az64,
    eventid integer NOT NULL ENCODE az64,
    dateid smallint NOT NULL ENCODE raw,
    numtickets smallint NOT NULL ENCODE az64,
    priceperticket numeric(8,2) ENCODE az64,
    totalprice numeric(8,2) ENCODE az64,
    listtime timestamp without time zone ENCODE az64
)
DISTSTYLE KEY
SORTKEY ( dateid );

LISTINGテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.listing
(
  listid INT64 NOT NULL,
  sellerid INT64 NOT NULL,
  eventid INT64 NOT NULL,
  dateid INT64 NOT NULL,
  numtickets INT64 NOT NULL,
  priceperticket NUMERIC,
  totalprice NUMERIC,
  listtime DATETIME
)
CLUSTER BY listid, dateid;

SALES テーブル

CREATE TABLE 文の内容は以下のような違いがあります。他のテーブルと同様の変換となります。

  • integer/smallint → INT64
  • numeric(8,2) → NUMERIC
  • timestamp without time zone → DATETIME
  • DISTSTYLE KEY → 無し
  • distkey/SORTKEY → CLUSTER
  • ENCODE az64 → 無し

SALESテーブル(変換前)

CREATE TABLE public.sales (
    salesid integer NOT NULL ENCODE az64,
    listid integer NOT NULL ENCODE az64 distkey,
    sellerid integer NOT NULL ENCODE az64,
    buyerid integer NOT NULL ENCODE az64,
    eventid integer NOT NULL ENCODE az64,
    dateid smallint NOT NULL ENCODE raw,
    qtysold smallint NOT NULL ENCODE az64,
    pricepaid numeric(8,2) ENCODE az64,
    commission numeric(8,2) ENCODE az64,
    saletime timestamp without time zone ENCODE az64
)
DISTSTYLE KEY
SORTKEY ( dateid );

SALESテーブル(変換後)

CREATE TABLE __DEFAULT_DATABASE__.public.sales
(
  salesid INT64 NOT NULL,
  listid INT64 NOT NULL,
  sellerid INT64 NOT NULL,
  buyerid INT64 NOT NULL,
  eventid INT64 NOT NULL,
  dateid INT64 NOT NULL,
  qtysold INT64 NOT NULL,
  pricepaid NUMERIC,
  commission NUMERIC,
  saletime DATETIME
)
CLUSTER BY listid, dateid;

Select 文

文字列連結をconcatに変換、複数テーブルの結合で明示的にCROSS JOINを記述、ORDER BYNULLS FIRSTを記述する点などがポイントです。

Select1(変換前)

SELECT  sellerid
       ,username
       ,(firstname ||' '|| lastname) AS name
       ,city
       ,SUM(qtysold)
FROM sales, date, users
WHERE sales.sellerid = users.userid
AND sales.dateid = date.dateid
AND year = 2008
AND city = 'San Diego'
GROUP BY  sellerid
         ,username
         ,name
         ,city
ORDER BY 5 desc
LIMIT 5;

Select1(変換後)

SELECT  sales.sellerid
       ,users.username
       ,concat(users.firstname,' ',users.lastname) AS name
       ,users.city
       ,SUM(sales.qtysold)                         AS sum
FROM __DEFAULT_DATABASE__.public.sales
CROSS JOIN __DEFAULT_DATABASE__.public.date
CROSS JOIN __DEFAULT_DATABASE__.public.users
WHERE sales.sellerid = users.userid
AND sales.dateid = date.dateid
AND date.year = 2008
AND users.city = 'San Diego'
GROUP BY  1
         ,2
         ,3
         ,4
ORDER BY 5 DESC NULLS FIRST
LIMIT 5;

複数テーブルの結合で明示的にCROSS JOINを記述する点などがポイントです。

Select2(変換前)

SELECT  catgroup
       ,SUM(qtysold) AS sold
FROM category c, event e, sales s
WHERE c.catid = e.catid
AND e.eventid = s.eventid
GROUP BY  1;

Select2(変換後)

SELECT  c.catgroup
       ,SUM(s.qtysold) AS sold
FROM __DEFAULT_DATABASE__.public.category AS c
CROSS JOIN __DEFAULT_DATABASE__.public.event AS e
CROSS JOIN __DEFAULT_DATABASE__.public.sales AS s
WHERE c.catid = e.catid
AND e.eventid = s.eventid
GROUP BY  1;

項目の連結をlistaggからstring_aggに変換、sales.selleridを明示的に文字列へキャストする点などがポイントです。

Select3(変換前)

SELECT  listagg(distinct sellerid,',') within group (order by sellerid)
FROM sales
WHERE eventid = 4337;

Select3(変換後)

SELECT  string_agg(DISTINCT CAST(sales.sellerid AS STRING),',' ORDER BY sales.sellerid) AS string_agg
FROM __DEFAULT_DATABASE__.public.sales
WHERE sales.eventid = 4337;

複数テーブルの結合で明示的にCROSS JOINを記述、JOINを明示的にINNER JOINと記述、ORDER BYNULLS LASTを記述する点などがポイントです。

Select4(変換前)

SELECT  catgroup1
       ,sold
       ,unsold
FROM
(
	SELECT  catgroup
	       ,SUM(qtysold) AS sold
	FROM category c, event e, sales s
	WHERE c.catid = e.catid
	AND e.eventid = s.eventid
	GROUP BY  catgroup
) AS a(catgroup1, sold)
JOIN
(
	SELECT  catgroup
	       ,SUM(numtickets)-SUM(qtysold) AS unsold
	FROM category c, event e, sales s, listing l
	WHERE c.catid = e.catid
	AND e.eventid = s.eventid
	AND s.listid = l.listid
	GROUP BY  catgroup
) AS b(catgroup2, unsold)
ON a.catgroup1 = b.catgroup2
ORDER BY 1;

Select4(変換後)

SELECT  a.catgroup1
       ,a.sold
       ,b.unsold
FROM
(
	SELECT  c.catgroup     AS catgroup1
	       ,SUM(s.qtysold) AS sold
	FROM __DEFAULT_DATABASE__.public.category AS c
	CROSS JOIN __DEFAULT_DATABASE__.public.event AS e
	CROSS JOIN __DEFAULT_DATABASE__.public.sales AS s
	WHERE c.catid = e.catid
	AND e.eventid = s.eventid
	GROUP BY  1
) AS a
INNER JOIN
(
	SELECT  c_0.catgroup                         AS catgroup2
	       ,SUM(l.numtickets) - SUM(s_0.qtysold) AS unsold
	FROM __DEFAULT_DATABASE__.public.category AS c_0
	CROSS JOIN __DEFAULT_DATABASE__.public.event AS e_0
	CROSS JOIN __DEFAULT_DATABASE__.public.sales AS s_0
	CROSS JOIN __DEFAULT_DATABASE__.public.listing AS l
	WHERE c_0.catid = e_0.catid
	AND e_0.eventid = s_0.eventid
	AND s_0.listid = l.listid
	GROUP BY  1
) AS b
ON a.catgroup1 = b.catgroup2
ORDER BY 1 NULLS LAST;

ORDER BYNULLS LASTを記述する点などがポイントです。

Select5(変換前)

WITH venuecopy AS
(
	SELECT  *
	FROM venue
)
SELECT  *
FROM venuecopy
ORDER BY 1
LIMIT 10;

Select5(変換後)

WITH venuecopy AS
(
	SELECT  venue.*
	FROM __DEFAULT_DATABASE__.public.venue
)
SELECT  venuecopy.*
FROM venuecopy
ORDER BY 1 NULLS LAST
LIMIT 10;

テーブル定義を含めて変換しているので、概ね妥当な書き換えとなっているように見受けられます。興味深い点としては、以下のような傾向があることかと思います。

  • 列定義の文字列の長さや精度の記述がなくなる
  • 暗黙の型変換を行わず、明示的にキャストする
  • CROSS JOININNER JOINを明示的に記述する

Interactive SQL translator の実行

次に Interactive SQL translator を使用してみます。こちらはCloud Console 上で対話的に変換できます。

Cloud Console で BigQuery の SQL Workspace を開きます。MOREからEnable SQL translationをクリックします。

Cloud Console で BigQuery の SQL Workspace を開きます。MOREからEnable SQL translationをクリックします。

次にMOREからTranslation settingsをクリックします。

Source diflectRedshiftを選択して、Processing Locationで実行するリージョンを選択します。そしてSAVEをクリックします。

Redshift から BigQuery の標準 SQL への変換を行う設定となりました。ここからは左のペインに Redshift のクエリを入力していきます。

バッチ変換で使用した Select3 のクエリを入力して、TRANSLATEをクリックして変換します。

Select3

SELECT  listagg(distinct sellerid,',') within group (order by sellerid)
FROM sales
WHERE eventid = 4337;

以下のように表示されます。しかしバッチ変換と異なりsales.selleridSTRINGへキャストしていません。これはテーブル定義が入力されていないためです。

SELECT
    string_agg(DISTINCT sales.sellerid, ',' ORDER BY sales.sellerid) AS string_agg
  FROM
    sales
  WHERE sales.eventid = 4337
;

Select3 の前に SALES テーブルの DDL を記述して変換してみます。

以下の Select 文となりバッチ変換と同様にsales.selleridSTRING型にキャストしています。発生しているエラーは BigQuery 上にpublicというデータセットが無いという真っ当な物なので特に問題ありません。

SELECT
    string_agg(DISTINCT CAST(sales.sellerid as STRING), ',' ORDER BY sales.sellerid) AS string_agg
  FROM
    public.sales
  WHERE sales.eventid = 4337
;

さいごに

BigQuery は Google Cloud の中でも特に人気の高いサービスであり、DWH をオンプレミスや他のサービスから移行する先のサービスとしてもよく使用されます。しかし、移行するためにはテーブル定義やクエリを変更する必要があるため、大きな時間とコストを使い、かつリスクもあるものでした。BigQuery Migration Service を使用してその時間とコストを低減しつつ安全に移行を行うことが可能になってきました。今後の BigQuery への移行作業では必須の機能と思われますので、ぜひ使用していきたいと思います。