LeetCode 2084 - Drop Type 1 Orders for Customers With Type 0 Orders
In this problem, we are given a database table named Orders. Each row represents a single order and contains three columns: - orderid, the unique identifier for the order - customerid, the customer who placed the order - ordertype, which is either 0 or 1 The task is to return…
Difficulty: 🟡 Medium
Topics: Database
Solution
Problem Understanding
In this problem, we are given a database table named Orders. Each row represents a single order and contains three columns:
order_id, the unique identifier for the ordercustomer_id, the customer who placed the orderorder_type, which is either0or1
The task is to return a filtered version of the table according to a specific business rule:
- If a customer has at least one order with
order_type = 0, then all of that customer's orders withorder_type = 1must be excluded. - If a customer does not have any type
0orders, then all of their orders should be included.
The important detail is that the filtering decision is made per customer, not per individual order. We first determine whether the customer has any type 0 orders, then decide which rows to keep.
The output can be returned in any order, which means we do not need an ORDER BY clause unless we want one for readability.
The input table may contain multiple orders per customer, and customers can have:
- only type
0orders - only type
1orders - a mixture of both
The constraints are not explicitly stated, but this is a standard SQL filtering problem where efficiency still matters. A naive solution that repeatedly scans the table for every row could become expensive for large datasets. The goal is to identify customers with type 0 orders efficiently, then filter rows based on that information.
Several edge cases are important:
- A customer with only type
1orders should have all their rows included. - A customer with both type
0and type1orders should only keep the type0rows. - A customer with multiple type
0orders should keep all of them. - A customer with exactly one order should still be handled correctly regardless of its type.
Approaches
Brute Force Approach
A straightforward solution is to examine every order individually and determine whether the same customer has any type 0 orders.
For each row in the table:
- Scan the entire table again.
- Check whether there exists another row with the same
customer_idandorder_type = 0. - If such a row exists:
- keep the current row only if its own
order_typeis0
- Otherwise:
- keep the row regardless of type
This approach is correct because every row independently verifies whether its customer belongs to the "has type 0 order" category.
However, the performance is poor because every row may require another full scan of the table. If there are n rows, this leads to O(n²) work.
Optimal Approach
The key observation is that the important information is whether a customer has at least one type 0 order.
Instead of repeatedly searching for this information, we can compute it once using a subquery or common table expression.
The optimal strategy is:
- Identify all customers who have at least one type
0order. - Keep:
- all type
0rows - type
1rows only for customers not in that set
This transforms the repeated lookup into a single preprocessing step. SQL handles this efficiently using IN, NOT IN, EXISTS, or joins.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | Repeatedly scans the table for every row |
| Optimal | O(n) | O(n) | Precomputes customers with type 0 orders |
Algorithm Walkthrough
Optimal SQL Algorithm
- First, identify all customers who have at least one order of type
0.
We can do this using a subquery:
SELECT customer_id
FROM Orders
WHERE order_type = 0
This creates the set of customers whose type 1 orders must be removed.
2. Next, scan through all rows in the Orders table.
For every row, we decide whether to keep it.
3. If the row has order_type = 0, always keep it.
The problem statement explicitly says that type 0 orders must remain.
4. If the row has order_type = 1, keep it only if the customer is not in the set of customers with type 0 orders.
This ensures that customers with mixed order types lose their type 1 rows.
5. Return the filtered rows.
Since the result may be returned in any order, no sorting is required.
Why it works
The algorithm works because every customer falls into exactly one of two categories:
- customers with at least one type
0order - customers without any type
0orders
For the first category, only type 0 rows are valid. For the second category, all rows are valid. The filtering condition exactly matches these rules, so every retained row is correct and every removed row is invalid.
Python Solution
Even though this is a Database problem, the following Python implementation demonstrates the equivalent logic programmatically.
from typing import List
class Solution:
def filterOrders(self, orders: List[List[int]]) -> List[List[int]]:
customers_with_type_zero = set()
for order_id, customer_id, order_type in orders:
if order_type == 0:
customers_with_type_zero.add(customer_id)
result = []
for order_id, customer_id, order_type in orders:
if order_type == 0:
result.append([order_id, customer_id, order_type])
elif customer_id not in customers_with_type_zero:
result.append([order_id, customer_id, order_type])
return result
The implementation follows the optimal algorithm directly.
The first loop identifies all customers who have at least one type 0 order. A Python set is used because membership checks are extremely fast, typically O(1).
The second loop builds the final answer. If an order is type 0, it is always included. If an order is type 1, it is included only when the customer does not appear in the set.
This mirrors the SQL filtering logic exactly.
Go Solution
package main
type Solution struct{}
func (s Solution) FilterOrders(orders [][]int) [][]int {
customersWithTypeZero := make(map[int]bool)
for _, order := range orders {
customerID := order[1]
orderType := order[2]
if orderType == 0 {
customersWithTypeZero[customerID] = true
}
}
result := [][]int{}
for _, order := range orders {
customerID := order[1]
orderType := order[2]
if orderType == 0 {
result = append(result, order)
} else if !customersWithTypeZero[customerID] {
result = append(result, order)
}
}
return result
}
The Go implementation follows the same two-pass strategy as the Python version.
A map[int]bool is used to store customers who have type 0 orders. Go maps provide efficient lookup performance similar to Python sets.
Slices are used for storing the result. Since Go does not have Python style dynamic lists, append is used to grow the slice as valid orders are found.
There are no integer overflow concerns here because the operations only involve comparisons and lookups.
SQL Solution
SELECT *
FROM Orders
WHERE order_type = 0
OR customer_id NOT IN (
SELECT customer_id
FROM Orders
WHERE order_type = 0
);
This query works by keeping:
- every row where
order_type = 0 - rows belonging to customers who never appear in the subquery result
The subquery identifies all customers who have at least one type 0 order.
Worked Examples
Example 1
Input table:
| order_id | customer_id | order_type |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 11 | 2 | 0 |
| 12 | 2 | 1 |
| 21 | 3 | 1 |
| 22 | 3 | 0 |
| 31 | 4 | 1 |
| 32 | 4 | 1 |
Step 1, Find customers with type 0 orders
We scan the table and collect customer IDs.
| Current Row | Action | Set Contents |
|---|---|---|
| (1,1,0) | add customer 1 | {1} |
| (2,1,0) | add customer 1 | {1} |
| (11,2,0) | add customer 2 | {1,2} |
| (12,2,1) | ignore | {1,2} |
| (21,3,1) | ignore | {1,2} |
| (22,3,0) | add customer 3 | {1,2,3} |
| (31,4,1) | ignore | {1,2,3} |
| (32,4,1) | ignore | {1,2,3} |
Final set:
{1, 2, 3}
Step 2, Filter rows
| Row | Keep? | Reason |
|---|---|---|
| (1,1,0) | Yes | type 0 always kept |
| (2,1,0) | Yes | type 0 always kept |
| (11,2,0) | Yes | type 0 always kept |
| (12,2,1) | No | customer 2 has type 0 |
| (21,3,1) | No | customer 3 has type 0 |
| (22,3,0) | Yes | type 0 always kept |
| (31,4,1) | Yes | customer 4 has no type 0 |
| (32,4,1) | Yes | customer 4 has no type 0 |
Final output:
| order_id | customer_id | order_type |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 11 | 2 | 0 |
| 22 | 3 | 0 |
| 31 | 4 | 1 |
| 32 | 4 | 1 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | One pass to collect customers, one pass to filter |
| Space | O(n) | The set of customers with type 0 orders may contain all customers |
The algorithm performs two linear scans of the input data. Each lookup in the hash set is constant time on average, so the overall runtime remains linear.
The additional memory comes from storing customer IDs that have at least one type 0 order. In the worst case, every customer may appear in the set.
Test Cases
def filter_orders(orders):
customers_with_type_zero = set()
for order_id, customer_id, order_type in orders:
if order_type == 0:
customers_with_type_zero.add(customer_id)
result = []
for order_id, customer_id, order_type in orders:
if order_type == 0:
result.append([order_id, customer_id, order_type])
elif customer_id not in customers_with_type_zero:
result.append([order_id, customer_id, order_type])
return result
# Example from problem statement
assert sorted(filter_orders([
[1, 1, 0],
[2, 1, 0],
[11, 2, 0],
[12, 2, 1],
[21, 3, 1],
[22, 3, 0],
[31, 4, 1],
[32, 4, 1]
])) == sorted([
[1, 1, 0],
[2, 1, 0],
[11, 2, 0],
[22, 3, 0],
[31, 4, 1],
[32, 4, 1]
]) # mixed customer types
# Customer with only type 1 orders
assert filter_orders([
[1, 1, 1],
[2, 1, 1]
]) == [
[1, 1, 1],
[2, 1, 1]
] # all rows retained
# Customer with only type 0 orders
assert filter_orders([
[1, 1, 0],
[2, 1, 0]
]) == [
[1, 1, 0],
[2, 1, 0]
] # all type 0 rows retained
# Customer with both types
assert filter_orders([
[1, 1, 0],
[2, 1, 1]
]) == [
[1, 1, 0]
] # type 1 removed
# Multiple customers with mixed data
assert sorted(filter_orders([
[1, 1, 1],
[2, 2, 0],
[3, 2, 1],
[4, 3, 1]
])) == sorted([
[1, 1, 1],
[2, 2, 0],
[4, 3, 1]
]) # independent customer handling
# Single order edge case
assert filter_orders([
[1, 1, 1]
]) == [
[1, 1, 1]
] # minimal input
# Empty input
assert filter_orders([]) == [] # no orders
| Test | Why |
|---|---|
| Mixed customer types | Validates the main filtering rule |
| Only type 1 orders | Ensures rows are preserved correctly |
| Only type 0 orders | Ensures type 0 rows are never removed |
| Customer with both types | Confirms type 1 rows are dropped |
| Multiple customers | Ensures filtering is customer-specific |
| Single order | Validates minimal non-empty input |
| Empty input | Confirms algorithm handles no data |
Edge Cases
Customers with only type 1 orders
A common mistake is accidentally removing all type 1 rows globally. The filtering rule only applies to customers who also have type 0 orders.
For example:
(31, 4, 1)
(32, 4, 1)
Customer 4 has no type 0 orders, so both rows must remain. The implementation handles this correctly because customer 4 never enters the customers_with_type_zero set.
Customers with both order types
This is the core edge case of the problem.
For example:
(11, 2, 0)
(12, 2, 1)
Once customer 2 is identified as having a type 0 order, every type 1 order for that customer must be excluded. The implementation correctly removes only the type 1 rows while preserving type 0.
Multiple type 0 orders
A customer may have several type 0 orders:
(1,1,0)
(2,1,0)
A buggy implementation might accidentally keep only one of them if it treats the problem as deduplication. However, the problem requires returning all valid rows. Since the algorithm filters rows individually and does not collapse duplicates, every type 0 row is preserved.
Empty input table
Although not shown in the constraints, robust solutions should handle an empty table gracefully.
If the input contains no rows, the set remains empty and the result list also remains empty. The algorithm naturally returns an empty result without special handling.