Skip to main content

Suspicious Bank Accounts - Solution & Explanation

MediumPremiumFree on FleetCodeDatabase5 min readAsked at: Jpmorgan
Practice this problem

Problem Statement

Table: Accounts

+----------------+------+
| Column Name    | Type |
+----------------+------+
| account_id     | int  |
| max_income     | int  |
+----------------+------+
account_id is the column with unique values for this table.
Each row contains information about the maximum monthly income for one bank account.

 

Table: Transactions

+----------------+----------+
| Column Name    | Type     |
+----------------+----------+
| transaction_id | int      |
| account_id     | int      |
| type           | ENUM     |
| amount         | int      |
| day            | datetime |
+----------------+----------+
transaction_id is the column with unique values for this table.
Each row contains information about one transaction.
type is ENUM (category) type of ('Creditor','Debtor') where 'Creditor' means the user deposited money into their account and 'Debtor' means the user withdrew money from their account.
amount is the amount of money deposited/withdrawn during the transaction.

 

A bank account is suspicious if the total income exceeds the max_income for this account for two or more consecutive months. The total income of an account in some month is the sum of all its deposits in that month (i.e., transactions of the type 'Creditor').

Write a solution to report the IDs of all suspicious bank accounts.

Return the result table in any order.

The result format is in the following example.

 

Example 1:

Input: 
Accounts table:
+------------+------------+
| account_id | max_income |
+------------+------------+
| 3          | 21000      |
| 4          | 10400      |
+------------+------------+
Transactions table:
+----------------+------------+----------+--------+---------------------+
| transaction_id | account_id | type     | amount | day                 |
+----------------+------------+----------+--------+---------------------+
| 2              | 3          | Creditor | 107100 | 2021-06-02 11:38:14 |
| 4              | 4          | Creditor | 10400  | 2021-06-20 12:39:18 |
| 11             | 4          | Debtor   | 58800  | 2021-07-23 12:41:55 |
| 1              | 4          | Creditor | 49300  | 2021-05-03 16:11:04 |
| 15             | 3          | Debtor   | 75500  | 2021-05-23 14:40:20 |
| 10             | 3          | Creditor | 102100 | 2021-06-15 10:37:16 |
| 14             | 4          | Creditor | 56300  | 2021-07-21 12:12:25 |
| 19             | 4          | Debtor   | 101100 | 2021-05-09 15:21:49 |
| 8              | 3          | Creditor | 64900  | 2021-07-26 15:09:56 |
| 7              | 3          | Creditor | 90900  | 2021-06-14 11:23:07 |
+----------------+------------+----------+--------+---------------------+
Output: 
+------------+
| account_id |
+------------+
| 3          |
+------------+
Explanation: 
For account 3:
- In 6-2021, the user had an income of 107100 + 102100 + 90900 = 300100.
- In 7-2021, the user had an income of 64900.
We can see that the income exceeded the max income of 21000 for two consecutive months, so we include 3 in the result table.

For account 4:
- In 5-2021, the user had an income of 49300.
- In 6-2021, the user had an income of 10400.
- In 7-2021, the user had an income of 56300.
We can see that the income exceeded the max income in May and July, but not in June. Since the account did not exceed the max income for two consecutive months, we do not include it in the result table.

Approach Overview

Problem Overview: You are given a transaction table for bank accounts. An account becomes suspicious if the total Creditor transactions exceed 10000 in two consecutive months. The task is to return all such account_id values.

Approach 1: Monthly Aggregation + Self Join (O(n log n) time, O(n) space)

Start by aggregating transactions per account and per month. Use SUM(amount) and group by account_id and a month key such as DATE_FORMAT(day, '%Y-%m'). Filter groups where the total creditor amount exceeds 10000. Once you have qualifying months, perform a self join on the aggregated result where the same account_id appears in two rows whose months are consecutive. This approach works well because the expensive scan happens only once during aggregation, and the self join only runs on the reduced dataset.

This pattern is common in database problems: aggregate first, then compare adjacent records. The main insight is converting raw transactions into a monthly summary table, which makes consecutive-month detection trivial.

Approach 2: Window Function with LAG (O(n log n) time, O(n) space)

Another clean solution uses SQL window functions. After computing the monthly creditor totals, apply LAG(month) over a partition of account_id ordered by month. This gives access to the previous month directly. If the previous month plus one equals the current month and both totals exceed 10000, the account is suspicious. Window functions reduce the need for explicit joins and keep the logic easier to read.

This approach relies on features commonly used in SQL analytics queries. Window functions like LAG or LEAD are particularly useful when comparing adjacent rows inside the same group.

Approach 3: Consecutive Month Detection with CTE (O(n log n) time, O(n) space)

You can also structure the query with a Common Table Expression. First CTE: compute monthly creditor totals. Second CTE: filter rows where the total exceeds 10000. Finally, detect consecutive months by joining rows where the month difference equals one. This structure keeps each transformation step isolated and readable, which helps when queries grow more complex.

CTEs are frequently used in window function and analytical SQL workflows because they make multi-step transformations easier to reason about.

Recommended for interviews: The monthly aggregation plus self-join approach is the most expected solution. It shows you understand grouping, filtering, and relational comparisons in SQL. Window functions demonstrate deeper SQL knowledge, but the aggregation + join method communicates the core logic clearly and works across most SQL engines.

Solution

Code

MySQL

Try this approach in the editor →

Detailed Complexity Analysis

ApproachTimeSpaceWhen to Use
Monthly Aggregation + Self JoinO(n log n)O(n)Most portable SQL solution; works across nearly all relational databases
Window Function with LAGO(n log n)O(n)Cleaner logic when the database supports window functions
CTE + Consecutive Month JoinO(n log n)O(n)Useful when structuring multi-step SQL transformations for readability

Video Solution

JP MORGAN LeetCode Medium “Suspicious Bank Accounts" 1843 Interview SQL Question Explanation | EDS • Everyday Data Science • 3,062 views views

Watch 3 more video solutions →

Frequently Asked Questions

Is Suspicious Bank Accounts easy or hard?
Suspicious Bank Accounts is considered a medium-level database problem. The main challenge is recognizing that you must first aggregate creditor transactions by month and then detect consecutive qualifying months for each account.
Suspicious Bank Accounts Python/Java solution
This problem is designed as a SQL database query rather than an algorithm implemented in Python or Java. The logic centers on aggregation, filtering, and comparing consecutive months directly inside the database using MySQL or similar SQL syntax.
How to solve Suspicious Bank Accounts in O(n)?
Pure O(n) performance is uncommon in SQL because grouping operations usually require sorting or hashing. The closest practical approach is aggregating monthly creditor totals and using window functions like LAG to compare adjacent months, which still behaves around O(n log n) in most database engines.
What is the best approach for Suspicious Bank Accounts?
The most common solution aggregates creditor transactions per account per month, filters totals above 10000, then checks for consecutive months using a self join. This approach is clear, portable across SQL engines, and runs in roughly O(n log n) time due to grouping and sorting operations.
Is Suspicious Bank Accounts asked at Google/Amazon/Meta?
Database aggregation and consecutive-period detection problems are common in SQL interviews at companies like Amazon, Meta, and Google. While the exact problem may vary, the pattern of grouping transactions and detecting consecutive periods appears frequently in data engineering and analytics interviews.
What data structure is used in Suspicious Bank Accounts?
The solution relies on relational database operations rather than traditional data structures. SQL grouping creates a temporary aggregated dataset keyed by account_id and month, which is then compared using joins or window functions.
What is the time complexity of Suspicious Bank Accounts?
The query typically runs in O(n log n) time. Aggregating transactions by account and month requires grouping and sorting, which dominates the cost. Space complexity is O(n) for storing the aggregated monthly results.

Ready to solve this problem?

Practice Suspicious Bank Accounts with our built-in code editor and test cases.

Practice on FleetCode