ExpertJoins50 points

Upgraded, regretted, downgraded

Sales at Cloudpeak celebrates every upgrade, but the pricing team suspects some customers upgrade, feel the pain of the higher bill and fall back within weeks. They want to see these reversals so they can interview the customers.

Dataset: Cloudpeak. Every plan change writes a new subscriptions row: the old row gets an end_date and the new row starts on that same date. A row with end_date NULL is the current plan. A customer who cancels has a last row whose end_date has no follow-up row.

Task: find every upgrade (a subscription row whose previous row of the same account had a lower mrr) that is followed directly by a downgrade (the next row of the same account has a lower mrr) starting no more than 90 days after the upgrade started. Return company, upgraded_on (start_date of the upgrade row), from_plan, upgraded_to, downgraded_on, downgraded_to and days_between (whole days from upgrade to downgrade). Sort by company, then upgraded_on.

The data you can query

Each table is shown with its column types and the first rows, so you can see exactly what the data looks like.

accounts40 rows
id INTEGERcompany TEXTcountry TEXTindustry TEXTsignup_date TEXT
1Aurora FreightGermanyLogistics2025-09-22
2Bluegrid EnergyUnited KingdomEnergy2025-10-13
3Cedar Health LabsUnited StatesHealthcare2026-03-07
4Delta Pay SystemsNigeriaFintech2025-12-26
5Evergreen Retail GroupCanadaRetail2025-02-12
6Falcon AerospaceUnited Arab EmiratesAerospace2025-02-12

Showing the first 6 of 40 rows.

events149 rows
id INTEGERaccount_id INTEGERuser_id INTEGERfeature TEXTevent_ts TEXT
1112api2025-09-24 16:05:34
2113api2026-04-29 12:15:40
3221api2025-10-15 15:14:48
4224sso2025-10-14 13:06:32
5224alerts2025-10-16 10:57:11
6333alerts2026-06-01 10:37:06

Showing the first 6 of 149 rows.

invoices168 rows
id INTEGERaccount_id INTEGERissued_on TEXTdue_on TEXTpaid_on TEXTamount_usd REAL
112026-01-012026-01-312026-01-22499
212026-02-012026-03-032026-02-13499
312026-03-012026-03-312026-04-23499
412026-04-012026-05-012026-04-23499
512026-05-012026-05-312026-05-20499
612026-06-012026-07-01NULL499

Showing the first 6 of 168 rows.

subscriptions65 rows
id INTEGERaccount_id INTEGERplan TEXTmrr REALstart_date TEXTend_date TEXT
11Scale4992025-09-22NULL
22Starter492025-10-13NULL
33Growth1992026-03-07NULL
44Growth1992025-12-26NULL
55Starter492025-02-122026-02-01
65Growth1992026-02-012026-05-01

Showing the first 6 of 65 rows.

tickets40 rows
id INTEGERaccount_id INTEGERopened_ts TEXTresolved_ts TEXTpriority TEXT
122025-11-28 08:44:00NULLNormal
2372026-01-29 15:51:002026-01-31 09:55:00Low
3182025-05-01 13:33:002025-05-04 09:13:00Low
4112025-09-24 16:09:002025-09-24 19:40:00Normal
5392025-12-06 15:34:002025-12-07 14:17:00High
6152026-03-20 11:06:002026-03-23 20:44:00Low

Showing the first 6 of 40 rows.

Your query runs on SQLite. Write a single SELECT; use LIMIT instead of TOP.

Need a hint?

Join the subscriptions table to itself three times: previous row, upgrade row, next row. The link between rows is an end_date equal to the next start_date.