bigquery勉強会 standard sql dialect

26
BigQuery勉強会 ~Standard SQL Dialect~ 2016/8/23 森下健 @ Sprocket 1

Upload: ken-morishita

Post on 16-Apr-2017

815 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: BigQuery勉強会 Standard SQL Dialect

BigQuery勉強会~Standard SQL Dialect~

2016/8/23森下健 @ Sprocket

1

Page 2: BigQuery勉強会 Standard SQL Dialect

今回の内容• Standard SQLの基本

• Standard SQLの便利機能を紹介

• (user, action, time) というイベントデータの処理例• 間隔ベースのセッションIDを付ける• ユーザやセッションの属性とイベントデータを⼀つのテーブルにする• あるAction Xが発⽣した後、時刻T以内にAction Zが発⽣する割合の集計

2

Page 3: BigQuery勉強会 Standard SQL Dialect

Standard SQLの基本

3

Page 4: BigQuery勉強会 Standard SQL Dialect

Standard SQL Dialect• 2016年6⽉にBeta公開された書き⽅

• ⼤規模かつ複雑な処理の記述が可能• (以前のをよく知らないので差分はわからないんですが)

• BQは処理が複雑でも料⾦同じなのでかなり使いみちがある

• なぜ「Standard」なのかと⾔われてもわかりません...

https://cloud.google.com/blog/big-data/2016/06/bigquery-111-now-with-standard-sql-iam-and-partitioned-tables4

Page 5: BigQuery勉強会 Standard SQL Dialect

Standard SQLモードに切り替えて使う

②チェックを外す①Show Optionsで開いて

5

Page 6: BigQuery勉強会 Standard SQL Dialect

Time Partitioning• _PARTITIONTIME というのがあるらしいが、よく知らない…• https://cloud.google.com/bigquery/docs/partitioned-tables

• 今の旧来のテーブルならば

という形で取れます

SELECT ...FROM `my_dataset.table_name_*` WHERE _TABLE_SUFFIX BETWEEN '20160720' AND FORMAT_DATE('%Y%m%d', CURRENT_DATE())

6

Page 7: BigQuery勉強会 Standard SQL Dialect

細かいこと• JSON_EXTRACT 関数が消えた

• DISTINCT がまともに動く感じになった

• /* ... */ でコメントになる

• 最後の結果セットをOrder Byできるサイズの上限は結構低い• 数千万レコードでもNGだったりする• ※ 完全ではないが対処法はある

• 昔はTipsとしてよく⾔われていた EACH とかもう不要

7

Page 8: BigQuery勉強会 Standard SQL Dialect

Standard SQLの便利機能 3つWITHAnalytic FunctionNest

8

Page 9: BigQuery勉強会 Standard SQL Dialect

WITH構⽂

SELECT ...FROM ( SELECT ... FROM (SELECT ...FROM original_data

) as A) as B

WITH A as (SELECT ...FROM original_data

)

, B as (SELECT ...FROM A

)

SELECT ...FROM B

■ 従来記法 ■ WITH記法

⼊れ⼦地獄から解放され、上から下に処理を書けるようになった 9

Page 10: BigQuery勉強会 Standard SQL Dialect

Analytic Function• 以前の Window Function (今回呼び名変わった?)• ※新しい機能ではないが紹介

• 普通のSQL(例えばMySQLの)ではできない、レコード間の演算ができる

10

Page 11: BigQuery勉強会 Standard SQL Dialect

11

user action time1 A 11 B 11 X 51 B 81 Z 92 A 22 B 52 D 10

例えば、ユーザ毎にアクセス順序でのSequence IDを付けたい

user action time seq1 A 1 11 B 1 21 X 5 31 B 8 41 Z 9 52 A 2 12 B 5 22 D 10 3

SELECT *, RANK() OVER (PARTITION BY user ORDER BY time) as seqFROM dataORDER BY user, seq

RANK():1から順に連番をふる

Page 12: BigQuery勉強会 Standard SQL Dialect

12

user action time1 A 11 B 11 X 51 B 81 Z 92 A 22 B 52 D 10

例えば、ユーザ毎に前回アクセスからの経過時刻をとりたい

WITH add_last_time as (SELECT *,

LAG(time) OVER (PARTITION BY user ORDER BY time) as last_timeFROM data

), add_span as (SELECT *,

time - last_time as spanFROM add_last_time

)select * from add_span ORDER BY user, time

user action time last_time span1 A 1 null null1 B 1 1 01 X 5 1 41 B 8 5 31 Z 9 8 12 A 2 null null2 B 5 2 32 D 10 5 5

LAG(X):⼀つ前のXの値を取得する

Page 13: BigQuery勉強会 Standard SQL Dialect

13

user action time1 A 11 B 11 X 51 B 81 Z 92 A 22 B 52 D 10

例えば、ユーザ毎に最初の2つのデータを残したい

WITH add_seq as (SELECT *, RANK() OVER (PARTITION BY user ORDER BY time) as seqFROM data

), omit_records as (SELECT * FROM add_seq WHERE seq <= 2

)SELECT * FROM omit_recordsORDER BY user, seq

user action time seq1 A 1 11 B 1 22 A 2 12 B 5 2

Page 14: BigQuery勉強会 Standard SQL Dialect

Nest (⼊れ⼦)• これがかなり便利

• 普通のSQLには無い感覚なので慣れは必要だが慣れるとデータのこねくり回しが捗ります

• 保存・Scanデータ量の削減になる• user_idのような⽂字列などの場合特に

14

Page 15: BigQuery勉強会 Standard SQL Dialect

15

例えば、Actionを⾏ったユーザをAction毎にまとめる(ユーザの重複OKの場合)

user action time1 A 11 B 31 X 51 B 201 Z 222 A 22 B 302 X 603 X 44 B 24 A 4

Row action users1 A 1

24

2 B 1124

3 X 123

4 Z 1

重複しているよ?

WITH action_users as (SELECT action, ARRAY_AGG(user) as usersFROM dataGROUP by action

)SELECT * FROM action_usersORDER BY action

ARRAY_AGG():GROUP BYと共に使い、値をARRAY型の1レコードにまとめる

Page 16: BigQuery勉強会 Standard SQL Dialect

16

例えば、Actionを⾏ったユーザをAction毎にまとめる(ユニークユーザの場合)

user action time1 A 11 B 31 X 51 B 201 Z 222 A 22 B 302 X 603 X 44 B 24 A 4

WITH action_users as (SELECT action, ARRAY_AGG(user) as usersFROM dataGROUP by action

), unique_action_users as (

SELECT action,ARRAY(

SELECT DISTINCT user FROM UNNEST(users) as user) as users

FROM action_users)SELECT * FROM unique_action_users ORDER BY action

Row action users1 A 1

24

2 B 124

3 X 123

4 Z 1

ARRAY():Sub Queryが単⼀列の複数(0~N)レコードを返す場合に使う.

UNNEST():ARRAY型を展開する。JOIN的になる場合とグループ単位で処理したい場合で少し意味合いが違う感じ.

Row action users1 A 1

24

2 B 1124

3 X 123

4 Z 1

Page 17: BigQuery勉強会 Standard SQL Dialect

■Group By する必要がある場合: ARRAY_AGG(STRUCT(...)) ... GROUP BY

SELECT ...,ARRAY_AGG(STRUCT(col1, col2, ...))

FROM ...GROUP BY X

複数列をArray型に⼊れる⽅法

■Group By する必要がない場合 or 何か計算処理する場合: ARRAY(SELECT STRUCT(...))

SELECT ...,ARRAY(

SELECT STRUCT(col1 * 2, SUM(col2) OVER ... , ...) FROM ...)

FROM ... STRUCT():レコード型を作る。レコード型という単⼀列になる。

Page 18: BigQuery勉強会 Standard SQL Dialect

(user, action, time) というイベントデータの処理例

18

Page 19: BigQuery勉強会 Standard SQL Dialect

19

間隔ベースのセッションIDを付ける

user action time1 A 11 B 31 X 51 B 201 Z 222 A 22 B 302 X 35

やりたいこと: ユーザ毎にアクセス間隔が10以上なら別のSessionIDを付けるようにしたい

1

212

user action time last_time new_session session_seq1 A 1 null 0 11 B 3 1 0 11 X 5 3 0 11 B 20 5 1 21 Z 22 20 0 22 A 2 null 0 12 B 30 2 1 22 X 35 30 0 2

WITH add_last_time as (SELECT *,

LAG(time) OVER (PARTITION BY user ORDER BY time) as last_timeFROM data

), add_span as (

SELECT *, IF(time - last_time >= 10, 1, 0) as new_sessionFROM add_last_time

)SELECT *, 1+SUM(new_session) OVER (PARTITION BY user ORDER BY time) as session_seqFROM add_span ORDER BY user, time

Page 20: BigQuery勉強会 Standard SQL Dialect

user session_seq session_start_time session_end_time session_time_span actions.action actions.time1 1 1 5 4 A 1

B 3X 5

1 2 20 22 2 B 20Z 22

2 1 2 2 0 A 22 2 30 35 5 B 30

20

ユーザやセッションの属性とイベントデータを⼀つのテーブルにする: Step1

user action time1 A 11 B 31 X 51 B 201 Z 222 A 22 B 302 X 35

やりたいこと:ユーザやセッションの属性とイベントデータを⼀つのテーブルにする

WITH ... 略 ..., add_session_seq as (

SELECT *, 1+SUM(new_session) OVER (PARTITION BY user ORDER BY time) as session_seqFROM add_span

)SELECT user, session_seq,

MIN(time) as session_start_time,MAX(time) as session_end_time,MAX(time) - MIN(time) as session_time_span,ARRAY_AGG(STRUCT(action, time)) as actions

FROM add_session_seqGROUP BY user, session_seq

Page 21: BigQuery勉強会 Standard SQL Dialect

user session_seq ... actions.action actions.time1 1 A 1

B 3X 5

1 2 B 20Z 22

2 1 A 22 2 B 30

21

ユーザやセッションの属性とイベントデータを⼀つのテーブルにする: Step2

やりたいこと:ユーザやセッションの属性とイベントデータを⼀つのテーブルにする

WITH ... 略 ..., group_by_user as (

SELECT user,MAX(session_seq) as session_num,ARRAY_AGG(STRUCT(

session_start_time, session_end_time, session_time_span, actions)) as sessions

FROM group_by_session GROUP BY user)SELECT * FROM group_by_userORDER BY user

user session_num sessions.session_start_time sessions.session_end_time sessions.session_time_span sessions.actions.action sessions.actions.time1 2 1 5 4 A 1

B 3X 5

20 22 2 B 20Z 22

2 2 2 2 0 A 230 35 5 B 30

X 35

Page 22: BigQuery勉強会 Standard SQL Dialect

22

ユーザ毎に, あるAction Xが発⽣した後、時刻T以内にAction Zが発⽣する割合の集計

やりたいこと: Xの後に時刻10以内にZが発⽣する割合を求める

user action time1 A 11 X 51 B 91 Z 112 A 22 X 152 Z 403 Z 33 X 64 X 44 X 54 Z 7

1

0 (時刻10以内でない)

0 (ZがXの後で発⽣していない)

1 (最初のXに対してのみ計算するとする)

X=4, Z=2 → 50% とカウントしたい

Page 23: BigQuery勉強会 Standard SQL Dialect

23

ユーザ毎に, あるAction Xが発⽣した後、時刻T以内にAction Zが発⽣する割合の集計

user action time1 A 11 X 51 B 91 Z 112 A 22 X 152 Z 403 Z 33 X 64 X 44 X 54 Z 7

user first_x_time action_times.action action_times.time1 5 X 5

Z 112 15 X 15

Z 403 6 Z 3

X 64 4 X 4

X 5Z 7

WITH group_by_user as (SELECT user, ARRAY_AGG(STRUCT(action, time)) as action_timesFROM data WHERE action in ('X', 'Z')

GROUP BY user), pickup_first_x_timeas (

SELECT user, action_times,(

SELECT MIN(time)FROM UNNEST(action_times) WHERE action='Xʼ

) as first_x_timeFROM group_by_user

)SELECT * FROM pickup_first_x_time ORDER BY user

Page 24: BigQuery勉強会 Standard SQL Dialect

24

ユーザ毎に, あるAction Xが発⽣した後、時刻T以内にAction Zが発⽣する割合の集計

user first_x_time action_times.action action_times.time1 5 X 5

Z 112 15 X 15

Z 403 6 Z 3

X 64 4 X 4

X 5Z 7

WITH ..., count_z as (

SELECT user, first_x_time,(

SELECT COUNT(*)FROM UNNEST(action_times) as aWHERE a.action = 'ZʼAND a.time BETWEEN first_x_time AND first_x_time + 10

) as z_count,action_times

FROM pickup_first_x_time)SELECT * FROM count_z ORDER BY user

user first_x_time z_count action_times.action action_times.time1 5 1 X 5

Z 112 15 0 X 15

Z 403 6 0 Z 3

X 64 4 1 X 4

X 5Z 7

Page 25: BigQuery勉強会 Standard SQL Dialect

25

WITH ...

SELECT COUNT(*) as x_cnt,COUNTIF(z_count > 0) as z_cnt

FROM count_z

user first_x_time z_count action_times.action action_times.time1 5 1 X 5

Z 112 15 0 X 15

Z 403 6 0 Z 3

X 64 4 1 X 4

X 5Z 7

ユーザ毎に, あるAction Xが発⽣した後、時刻T以内にAction Zが発⽣する割合の集計

x_cnt z_cnt4 2

Page 26: BigQuery勉強会 Standard SQL Dialect

まとめ• Standard SQLは結構強い

• まだβ版なので仕様が変わる可能性もあるが、考え⽅などは変わらないので慣れておくのは良いこと

• 是⾮活⽤していきましょう!

26