dbt 中 unique key 的機制

淺談 dbt 在 RedShift 上 unique key 的運行機制

前些日子在跑 dbt 的時候遇到了幾張表格資料數量跟預期不同的地方,後來發現都跟 unique_key 有關係。

其實人家文件寫得算很清楚了,就怪當初自己沒仔細讀:

A unique_key enables updating existing rows instead of just appending new rows. If new information arrives for an existing unique_key, that new information can replace the current information instead of being appended to the table. If a duplicate row arrives, it can be ignored.

欸,資料少了

這是最初的蠢問題,某張表格在設計的時候沒有仔細思考,後來被回報資料有缺漏。

而原因即是具有 unique_key 的 incremental table 在新增資料的過程會執行一個長得有點像 upsert) 的操作,因而導致舊資料列不見。

欸,資料多了

這是過了一段時間之後發生的問題——在認知到 unique_key 會有更新既有資料列的行為之後。

但這次的問題是資料多了,在某次跑 full refresh 後測試結果報錯,追了老半天才注意到 unique key 不能保證唯一——而其發生的條件還會跟使用的資料庫有關。

incremental_strategy 段落中有說到 SnowflakeBigQuery 會採用 MERGE 來跑 unique key;在一次的資料轉換中若發生有多筆資料在 unique key 欄位上具有相同的值,則分別會有其對應的機制,以 BigQuery 而言它會自動以較晚抵達的資料覆蓋,即具有跟 upsert 極為相似特性。

但當然不是永遠這麼好康,我操作的環境是 RedShift,其底層是 PostreSQL,在這個世界裡我們沒有 MERGE 語法。追到了原始碼後注意到只有 delete+insert 可用,而其操作就是把目標表格中所有值有碰撞到的列刪除後、再將抽出的資料全部放入目標表格中。

故在 RedShift / PostreSQL 下,在同一次執行中有多個列在 unique key 欄位上具有同樣的值時,這些列就都會被放入表格中:

但這如果未來又有新的列來了,這些「多出來」的列又都會被刪掉:

不過這個操作也已經是 RedShift 官方推薦的手段了。