Many clients use Google Sheets as a lightweight CRM. To build real analytics, I needed an automated pipeline in n8n that periodically syncs modified rows into an internal PostgreSQL database.
The Problem: Duplicate Primary Key Collisions
A naive sync workflow crashes whenever an existing row is re-saved, throwing duplicate key value violates unique constraint. Conversely, deleting and re-inserting thousands of rows kills database performance.
The Fix: PostgreSQL Upsert (ON CONFLICT DO UPDATE)
In the n8n Postgres node, instead of running a basic Insert query, I configured an Upsert operation using the unique row ID.
INSERT INTO crm_leads (lead_id, email, full_name, status, updated_at)
VALUES ($1, $2, $3, $4, NOW())
ON CONFLICT (lead_id)
DO UPDATE SET
email = EXCLUDED.email,
full_name = EXCLUDED.full_name,
status = EXCLUDED.status,
updated_at = NOW();
Outcome
The sync now runs incrementally every 15 minutes. It updates existing leads and inserts new ones without duplicates or locked tables.