2889 字
14 分鐘
n8n 踩坑筆記:Merge 節點搭配 Loop 的常見陷阱與解法

在 n8n 建構複雜工作流程時,Merge 節點和 Loop Over Items 是兩個非常強大的工具。Merge 可以合併多個資料來源,Loop 則讓你能夠逐項處理資料。但當這兩個節點一起使用時,卻容易踩到一些令人困惑的陷阱。

我最近在處理一個需要合併多個資料源並迴圈處理的流程時,遇到了兩個看似不可思議的問題:明明預期會合併成一批資料,結果卻被分成多批;明明資料的 ID 都對得上,Join 卻沒有成功。

這篇文章記錄這些問題的根本原因和解決方案,幫助你避開這些陷阱。

問題一:Merge 輸出資料被分成多批#

問題描述#

假設我有 5 個活動資料需要處理,流程設計如下:

活動資料 (5 筆)
IF 節點分流
分支 A (2 筆) → 處理 → Merge
分支 B (3 筆) → 處理 → Merge

預期結果:Merge 節點輸出 1 批,包含 5 筆資料

實際結果:Merge 節點輸出 2 批,第一批 2 筆,第二批 3 筆

讓我們用表格對比一下預期與實際的差異:

情況預期輸出實際輸出問題
5 個活動經過 IF 分流1 批資料(5 筆)2 批資料(2 筆 + 3 筆)❌ 資料被分批
後續節點處理一次處理完所有資料需要分兩次處理⚠️ 流程複雜化

原因:n8n v1 的深度優先執行順序#

這個問題的根源在於 n8n v1 的執行順序是深度優先(Depth-First),而不是我們直覺認為的廣度優先。

實際執行流程:

IF 節點分流 5 個活動(2 個走分支 A,3 個走分支 B)
n8n 先「完整」跑完分支 A(2 個)
↓ 到達 Merge → 輸出第一批 [2 筆]
然後才跑分支 B(3 個)
↓ 到達 Merge → 輸出第二批 [3 筆]

Merge 節點的 Append 模式不會「等待」所有分支的資料到齊,而是有資料進來就立即輸出。因為 n8n 是深度優先執行,所以第一個分支完成時 Merge 就會輸出一次,第二個分支完成時再輸出一次。

重要觀念

n8n v1 是深度優先執行:先完整跑完一個分支到底,再回來跑下一個分支。分支的執行順序由畫布上的位置決定(從上到下、從左到右)。

解決方案#

方法一:用 Loop Over Items 包住整個流程(推薦)#

這是最可靠的解決方案。透過將整個流程包在 Loop Over Items 中,強制讓每一筆資料都完整走完所有分支後,才處理下一筆資料。

正確的工作流程架構:

資料來源 (5 筆)
Loop Over Items (batch size: 1) ← 新增
↓ (每次只處理 1 筆)
IF 判斷分流
各自的處理流程
回到 Loop Over Items
Loop 的 "done" 輸出 → 自動收集所有 5 筆
後續處理(排序等)

優勢:

  • ✅ Loop 會自動收集所有迭代的結果
  • ✅ 保證輸出是單一批次
  • ✅ 執行順序可預測
  • ✅ 不需要額外的 Merge 或 Aggregate 節點

為什麼不能用 Aggregate 解決?#

你可能會想:「在 Merge 後面加個 Aggregate 不就好了?」

分支 A → Merge → Aggregate → 單一批次輸出?
分支 B ──┘

❌ 這個方法無效!

因為 Aggregate 本身也會被執行多次。當第一批資料(2 筆)到達 Aggregate 時,Aggregate 就會執行並輸出;當第二批資料(3 筆)到達時,Aggregate 又會再執行一次。所以你還是會得到 2 批資料,只是每批被「打包」成單一項目而已。

執行順序:

步驟發生的事Aggregate 輸出
1分支 A 完成 → Merge 輸出第一批(2 筆)Aggregate 執行 → 輸出第 1 個打包項目
2分支 B 完成 → Merge 輸出第二批(3 筆)Aggregate 再執行 → 輸出第 2 個打包項目
結果❌ 還是 2 批,只是變成 2 個打包項目並沒有合併成單一批次
重要提醒

Aggregate 無法解決「分批」問題,因為它本身也會隨著輸入的批次而被多次執行。唯一可靠的解法是使用 Loop Over Items 包裝整個流程

最佳實踐建議

如果你的流程需要確保所有資料合併成單一批次,必須使用 Loop Over Items 包裝整個流程。這是唯一穩定且可預測的做法,沒有其他捷徑。

問題二:Merge Join 時部分資料沒匹配到#

問題描述#

我想使用 Merge 節點的 Combine 模式,透過 category_id 欄位來 join 兩個資料來源:

活動資料(含 category_id)
類別資料(含 category_id) → Merge (Combine mode, join by category_id)
期望:活動資料 + 對應的類別資訊

但實際執行後,有些活動明明有對應的 category_id,卻沒有成功合併到類別資訊。

根本原因:Merge 放在 Loop 內,但 Input 2 在 Loop 外#

這是最容易被忽略,但影響最嚴重的問題! 大多數 Join 失敗的情況都源於這個架構錯誤。

錯誤架構示意#

❌ 錯誤架構
類別資料 (Loop 外,只執行 1 次) ──┐
↓ Input 2
Loop Over Items │
↓ (loop) │
處理活動... ─────────────────→ Merge (在 Loop 內)
↓ ↑ Input 1
回到 Loop

問題分析:Loop 每次迭代時發生了什麼?

Loop 迭代Input 1 (活動資料)Input 2 (類別資料)Merge 結果原因
第 1 次✅ 活動 1✅ 全部類別資料✅ 合併成功類別資料還在
第 2 次✅ 活動 2❌ 空的❌ 沒東西可 join類別資料被第 1 次消耗掉了
第 3 次✅ 活動 3❌ 空的❌ 沒東西可 join類別資料被第 1 次消耗掉了
第 4 次✅ 活動 4❌ 空的❌ 沒東西可 join類別資料被第 1 次消耗掉了
第 5 次✅ 活動 5❌ 空的❌ 沒東西可 join類別資料被第 1 次消耗掉了

為什麼類別資料會被「消耗」掉?

因為類別資料的節點在 Loop 外面,只會執行一次。當 Loop 第一次迭代時,Merge 節點會取得並「使用」這份類別資料。但在第二次迭代時,n8n 不會重新執行 Loop 外的節點,所以 Input 2 就變成空的了。

解決方案:把 Merge 移到 Loop 外#

正確的架構應該是:

✅ 正確架構
類別資料 (只取 1 次) ─────────────┐
↓ Input 2
Loop Over Items │
↓ (loop) │
處理活動... │
↓ │
回到 Loop │
↓ │
Loop "done" 輸出 (全部活動) ──────→ Merge (在 Loop 外)
↓ ↑ Input 1
後續處理...

為什麼這樣可以運作?

元素執行次數資料狀態結果
類別資料節點1 次完整保留,不會被消耗
Loop 處理活動5 次(每筆一次)每次處理一筆活動
Loop “done” 輸出1 次輸出全部 5 筆活動
Merge1 次5 筆活動 × 完整類別資料✅ 全部都能 join

這樣的架構確保:

  • ✅ 類別資料只取一次,完整保留不會被消耗
  • ✅ 活動資料經過 Loop 處理後一次輸出全部
  • ✅ Merge 在 Loop 外執行,能夠正確 join 所有資料
重要原則

Loop 內需要重複使用的資料,來源可以放 Loop 外,但 Merge 必須放 Loop 外!

如果把 Merge 放在 Loop 內,Loop 外的資料只會在第一次迭代時有效,後續迭代會失去資料。

補充:其他可能的 Join 失敗原因#

如果你已經確認架構正確(Merge 在 Loop 外),但 Join 還是失敗,可以檢查以下項目:

資料類型不一致#

有些情況下,兩邊要 join 的欄位資料類型可能不一致

來源category_id 型別範例值
Google Sheetsstring(字串)"1", "2", "3"
API 回傳number(數字)1, 2, 3

當一邊是 "1" (string),另一邊是 1 (number) 時,Merge 會認為它們不匹配("1" !== 1)。

解決方法:開啟 Fuzzy Compare

Merge 節點提供了 Fuzzy Compare 功能來自動處理型別差異:

  1. 打開 Merge 節點
  2. 展開 Options 區塊
  3. 開啟 Fuzzy Compare

或者手動統一型別:

// ✅ 統一轉換為 number
{{ Number($json.category_id) }}
// 或統一轉換為 string
{{ String($json.category_id) }}

其他檢查項目#

  • 欄位名稱拼寫:確認兩邊的欄位名稱完全一致(包含大小寫)
  • 空值處理:確認資料中沒有 nullundefined
  • 前後空白:字串值可能有前後空白(如 " 1 " vs "1"
除錯建議

在 Merge 前加一個 Code 節點,用 console.log() 檢查兩邊要 join 的欄位值和型別,可以快速找出不匹配的原因。

重要原則總結#

原則 1:理解 n8n v1 執行順序#

n8n v1 是**深度優先(Depth-First)**執行:先完整跑完一個分支到底,再回來跑下一個分支。

執行順序受什麼影響?

  • 畫布上的節點位置(從上到下、從左到右)
  • 分支的繪製順序

實務建議:

  • 如果需要精確控制執行順序,使用 Loop Over Items 包裝
  • 不要假設分支會「同時」執行

原則 2:Loop + Merge 的正確搭配#

正確的架構模式:

✅ 正確模式
Loop 外的資料 ──┐
Loop 處理中... │
Loop done ─────→ Merge (Loop 外)
└──→ 後續處理
❌ 錯誤模式
Loop 外的資料 ──→ Merge (Loop 內) ← 只有第一次迭代有資料
Loop 處理中... ──┘

記憶口訣:

Loop 內需要重複使用的資料,來源可以放 Loop 外,但 Merge 必須放 Loop 外

原則 3:Join 前檢查資料類型#

在使用 Merge Combine 模式進行 join 時:

必做檢查清單:

  • 兩邊要 join 的欄位資料型別是否一致(string vs number)
  • 是否需要開啟 Fuzzy Compare
  • 是否需要在 Merge 前統一轉換型別

型別統一方法:

// ✅ 方法 1:在 Set 節點或 Code 節點中轉換
{{ Number($json.category_id) }}
// ✅ 方法 2:開啟 Merge 的 Fuzzy Compare 選項

除錯技巧#

遇到 Merge 或 Loop 的問題時,可以使用以下技巧快速定位:

技巧 1:檢查 Merge 的兩個 Input#

點開 Merge 節點,分別查看 Input 1 和 Input 2 的資料:

  • 確認兩邊的資料筆數
  • 確認要 join 的欄位值和型別
  • 確認資料是否為空

如何檢查:

  1. 點擊 Merge 節點
  2. 在右側面板點選 “Input 1” 或 “Input 2”
  3. 查看資料內容和結構

技巧 2:觀察節點執行次數#

如果 Merge 節點被執行了多次,代表資料是分批進來的:

  • 查看節點左上角的執行次數標記
  • 如果看到 “1/2”、“2/2” 這樣的標記,代表 Merge 執行了 2 次
  • 這通常代表你遇到了「問題一」的情況

技巧 3:使用 Sticky Note 標註#

在複雜流程中,使用 Sticky Note(便利貼)標註哪些節點在 Loop 內、哪些在 Loop 外:

┌─────────────────────┐
│ Loop 外的節點 │
│ - 類別資料取得 │
└─────────────────────┘
┌─────────────────────┐
│ Loop 內的節點 │
│ - IF 判斷 │
│ - 資料處理 │
└─────────────────────┘
┌─────────────────────┐
│ Loop 外的節點 │
│ - Merge │
│ - 最終處理 │
└─────────────────────┘

這樣可以幫助你和團隊快速理解流程架構,避免踩坑。


希望這篇文章能幫助你避開 n8n Merge + Loop 的常見陷阱!如果你在使用 n8n 時遇到其他問題,歡迎交流討論。


參考資料:

n8n 踩坑筆記:Merge 節點搭配 Loop 的常見陷阱與解法
https://laplusda.com/posts/n8n-merge-loop-pitfalls/
作者
Zero
發佈於
2025-12-26
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。