Window関数便利ですよね。
便利すぎてめっちゃ使ってたら最近あることにハマったので記録しておきます。
ぶつかった問題
LAST_VALUE
を使って、ユーザごとの最後のアクションを知りたい。
ということでこんなクエリを出したけど、なぜかLAST_VALUE
の値が自分自身になる
SELECT
user_id
,td_url
,time
,LAST_VALUE(td_url) OVER (
PARTITION BY user_id
ORDER BY time
) AS last_url
FROM pageviews
予想
user_id | time | td_url | last_url
------- | ------ | -------- | --------
AAA | 00:01 | url_1 | url_3
AAA | 00:02 | url_2 | url_3
AAA | 00:03 | url_3 | url_3
BBB | 00:01 | url_1 | url_1
結果
user_id | time | td_url | last_url
------- | ------ | -------- | --------
AAA | 00:01 | url_1 | url_1
AAA | 00:02 | url_2 | url_2
AAA | 00:03 | url_3 | url_3
BBB | 00:01 | url_1 | url_1
・・・?!
いろいろ試行錯誤したところ、
-
FIEST_VALUE
を確認しが、それはうまくいく。なぜFIRST
がいけてLAST
がいけないのか... - 同じように
MAX
も値が意図しないものになったけどMIN
は正しい。「最後」を取る系がだめ? -
RANK
やLEG
なども問題ない。PARTITIONの設定などはおかしくなさそう
解決策
-
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
をつける
SELECT
user_id
,LAST_VALUE(td_url) OVER (
PARTITION BY user_id
ORDER BY time
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_url
FROM pageviews
答えは、公式リファレンス:3つめの黒ポチ にありました。
-
OVER
内のクエリには、デフォルトでRANGE UNBOUNDED PRECEDING
がつく(明示しないとこれになる) - これは
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
と同義で、PARTITION
で区切ったうちの「1つ目」から「自分」までしか調べませんよ、という意味 -
FIRST_VALUE
が成功したのは、FIRST
=「1つ目」さえ取れていれば正しい値が取れるから - だからTreasureDataの人もなるべく
FIRST_VALUE
使ってねって言ってくれている - 実際
CURRENT ROW
にしたら正しくない値に戻った - でも実際使うときにこれくっつけるの長いし忘れそうなので、
DESC
とFIRST_VALUE
が良さそう
SELECT
user_id
,td_url
,time
,FIRST_VALUE(td_url) OVER (
PARTITION BY user_id
ORDER BY time DESC
) AS last_url
FROM pageviews
結果
user_id | time | td_url | last_url
------- | ------ | -------- | --------
AAA | 00:01 | url_1 | url_3
AAA | 00:02 | url_2 | url_3
AAA | 00:03 | url_3 | url_3
BBB | 00:01 | url_1 | url_1
よしよし
結論
ドキュメントをよく読もうな