INSERT ON CONFLICT (upsert) in PostgreSQL - Time & Space Complexity
When we use INSERT ON CONFLICT in PostgreSQL, we want to add new data or update existing data without duplicates.
We ask: How does the time to do this grow as the data gets bigger?
Analyze the time complexity of the following code snippet.
INSERT INTO users (id, name, email)
VALUES (1, 'Alice', 'alice@example.com')
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, email = EXCLUDED.email;
This code tries to insert a user. If the user id already exists, it updates the name and email instead.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Searching for a matching row by the conflict key (id).
- How many times: Once per insert or update operation.
As the table grows, finding if the id exists takes longer if no index is used.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 checks if no index |
| 100 | About 100 checks if no index |
| 1000 | About 1000 checks if no index |
Pattern observation: Without an index, the search grows linearly with table size. With an index, it stays fast.
Time Complexity: O(log n)
This means the time to insert or update grows slowly as the table grows, thanks to the index.
[X] Wrong: "INSERT ON CONFLICT always takes the same time no matter the table size."
[OK] Correct: Without an index on the conflict column, the database must scan more rows as the table grows, making it slower.
Understanding how upsert works and its time cost shows you know how databases handle data efficiently, a useful skill in many projects.
"What if we removed the index on the conflict column? How would the time complexity change?"
Practice
INSERT ON CONFLICT clause do in PostgreSQL?Solution
Step 1: Understand the purpose and behavior of INSERT ON CONFLICT
This clause helps handle duplicate key conflicts during insert operations by allowing an update instead of error. It either updates the existing row or ignores the insert, preventing errors.Final Answer:
It inserts a new row or updates existing row if a conflict occurs. -> Option AQuick Check:
INSERT ON CONFLICT = Insert or update [OK]
- Confusing ON CONFLICT with DELETE
- Assuming it only inserts without update
- Thinking it creates new tables
price on conflict for a table products with unique id?Solution
Step 1: Check PostgreSQL syntax for upsert and identify correct usage of EXCLUDED
PostgreSQL uses ON CONFLICT (column) DO UPDATE SET with EXCLUDED to refer to new values. INSERT INTO products (id, price) VALUES (1, 100) ON CONFLICT (id) DO UPDATE SET price = EXCLUDED.price; correctly uses EXCLUDED.price to update the price on conflict of id.Final Answer:
INSERT INTO products (id, price) VALUES (1, 100) ON CONFLICT (id) DO UPDATE SET price = EXCLUDED.price; -> Option CQuick Check:
Correct syntax uses ON CONFLICT (id) DO UPDATE SET with EXCLUDED [OK]
- Using MySQL syntax ON DUPLICATE KEY
- Forgetting EXCLUDED keyword
- Using DO DELETE which is invalid
users(id INT PRIMARY KEY, name TEXT, age INT) with data:(1, 'Alice', 30)What will be the result of this query?
INSERT INTO users (id, name, age) VALUES (1, 'Alice', 31) ON CONFLICT (id) DO UPDATE SET age = EXCLUDED.age RETURNING *;
Solution
Step 1: Understand the existing data/conflict and apply ON CONFLICT DO UPDATE
Row with id=1 exists with age=30, new insert tries age=31 causing conflict on id. It updates age to EXCLUDED.age (31) and returns the updated row.Final Answer:
[{id:1, name:'Alice', age:31}] -> Option BQuick Check:
ON CONFLICT updates age to 31 and returns updated row [OK]
- Expecting error on duplicate key
- Thinking old age remains unchanged
- Assuming empty result on update
INSERT INTO inventory (product_id, quantity) VALUES (10, 5) ON CONFLICT (product_id) DO UPDATE quantity = quantity + EXCLUDED.quantity;
Solution
Step 1: Review ON CONFLICT DO UPDATE syntax and identify missing SET keyword
The DO UPDATE clause requires SET keyword before specifying column updates. The query misses SET before 'quantity = quantity + EXCLUDED.quantity', causing syntax error.Final Answer:
Missing SET keyword before update expressions. -> Option DQuick Check:
DO UPDATE requires SET before column assignments [OK]
- Omitting SET keyword in DO UPDATE
- Misusing EXCLUDED keyword
- Confusing DO NOTHING and DO UPDATE
users(id, name, score) but if the id exists, update the score only if the new score is higher. Which query correctly implements this?Solution
Step 1: Understand conditional update on conflict and use WHERE clause in DO UPDATE
You want to update score only if new score is greater than existing score. INSERT INTO users (id, name, score) VALUES (2, 'Bob', 50) ON CONFLICT (id) DO UPDATE SET score = EXCLUDED.score WHERE EXCLUDED.score > users.score; uses WHERE EXCLUDED.score > users.score to conditionally update score correctly.Final Answer:
INSERT INTO users (id, name, score) VALUES (2, 'Bob', 50) ON CONFLICT (id) DO UPDATE SET score = EXCLUDED.score WHERE EXCLUDED.score > users.score; -> Option AQuick Check:
Conditional update uses WHERE clause in DO UPDATE [OK]
- Updating unconditionally without WHERE
- Using DO NOTHING with WHERE (invalid)
- Setting score to old value mistakenly
Ready to prove it?
Pass the test and this pattern moves unknown
