Skip to content

Commit 01e707c

Browse files
authored
Added task 3497
1 parent cd4d4dd commit 01e707c

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
3497\. Analyze Subscription Conversion
2+
3+
Medium
4+
5+
Table: `UserActivity`
6+
7+
+------------------+---------+
8+
| Column Name | Type |
9+
+------------------+---------+
10+
| user_id | int |
11+
| activity_date | date |
12+
| activity_type | varchar |
13+
| activity_duration| int |
14+
+------------------+---------+
15+
(user_id, activity_date, activity_type) is the unique key for this table. activity_type is one of ('free_trial', 'paid', 'cancelled').
16+
activity_duration is the number of minutes the user spent on the platform that day.
17+
Each row represents a user's activity on a specific date.
18+
19+
A subscription service wants to analyze user behavior patterns. The company offers a `7`\-day **free trial**, after which users can subscribe to a **paid plan** or **cancel**. Write a solution to:
20+
21+
1. Find users who converted from free trial to paid subscription
22+
2. Calculate each user's **average daily activity duration** during their **free trial** period (rounded to `2` decimal places)
23+
3. Calculate each user's **average daily activity duration** during their **paid** subscription period (rounded to `2` decimal places)
24+
25+
Return _the result table ordered by_ `user_id` _in **ascending** order_.
26+
27+
The result format is in the following example.
28+
29+
**Example:**
30+
31+
**Input:**
32+
33+
UserActivity table:
34+
35+
| user_id | activity_date | activity_type | activity_duration |
36+
|---------|---------------|---------------|-------------------|
37+
| 1 | 2023-01-01 | free_trial | 45 |
38+
| 1 | 2023-01-02 | free_trial | 30 |
39+
| 1 | 2023-01-05 | free_trial | 60 |
40+
| 1 | 2023-01-10 | paid | 75 |
41+
| 1 | 2023-01-12 | paid | 90 |
42+
| 1 | 2023-01-15 | paid | 65 |
43+
| 2 | 2023-02-01 | free_trial | 55 |
44+
| 2 | 2023-02-03 | free_trial | 25 |
45+
| 2 | 2023-02-07 | free_trial | 50 |
46+
| 2 | 2023-02-10 | cancelled | 0 |
47+
| 3 | 2023-03-05 | free_trial | 70 |
48+
| 3 | 2023-03-06 | free_trial | 60 |
49+
| 3 | 2023-03-08 | free_trial | 80 |
50+
| 3 | 2023-03-12 | paid | 50 |
51+
| 3 | 2023-03-15 | paid | 55 |
52+
| 3 | 2023-03-20 | paid | 85 |
53+
| 4 | 2023-04-01 | free_trial | 40 |
54+
| 4 | 2023-04-03 | free_trial | 35 |
55+
| 4 | 2023-04-05 | paid | 45 |
56+
| 4 | 2023-04-07 | cancelled | 0 |
57+
58+
**Output:**
59+
60+
| user_id | trial_avg_duration | paid_avg_duration |
61+
|---------|--------------------|-------------------|
62+
| 1 | 45.00 | 76.67 |
63+
| 3 | 70.00 | 63.33 |
64+
| 4 | 37.50 | 45.00 |
65+
66+
**Explanation:**
67+
68+
* **User 1:**
69+
* Had 3 days of free trial with durations of 45, 30, and 60 minutes.
70+
* Average trial duration: (45 + 30 + 60) / 3 = 45.00 minutes.
71+
* Had 3 days of paid subscription with durations of 75, 90, and 65 minutes.
72+
* Average paid duration: (75 + 90 + 65) / 3 = 76.67 minutes.
73+
* **User 2:**
74+
* Had 3 days of free trial with durations of 55, 25, and 50 minutes.
75+
* Average trial duration: (55 + 25 + 50) / 3 = 43.33 minutes.
76+
* Did not convert to a paid subscription (only had free\_trial and cancelled activities).
77+
* Not included in the output because they didn't convert to paid.
78+
* **User 3:**
79+
* Had 3 days of free trial with durations of 70, 60, and 80 minutes.
80+
* Average trial duration: (70 + 60 + 80) / 3 = 70.00 minutes.
81+
* Had 3 days of paid subscription with durations of 50, 55, and 85 minutes.
82+
* Average paid duration: (50 + 55 + 85) / 3 = 63.33 minutes.
83+
* **User 4:**
84+
* Had 2 days of free trial with durations of 40 and 35 minutes.
85+
* Average trial duration: (40 + 35) / 2 = 37.50 minutes.
86+
* Had 1 day of paid subscription with duration of 45 minutes before cancelling.
87+
* Average paid duration: 45.00 minutes.
88+
89+
The result table only includes users who converted from free trial to paid subscription (users 1, 3, and 4), and is ordered by user\_id in ascending order.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Write your MySQL query statement below
2+
# #Medium #2025_03_29_Time_347_ms_(100.00%)_Space_0.0_MB_(100.00%)
3+
SELECT
4+
ft.user_id,
5+
ROUND(ft.avg_trial, 2) AS trial_avg_duration,
6+
ROUND(pt.avg_paid, 2) AS paid_avg_duration
7+
FROM
8+
(SELECT user_id, AVG(activity_duration) AS avg_trial
9+
FROM UserActivity
10+
WHERE activity_type = 'free_trial'
11+
GROUP BY user_id) ft
12+
JOIN
13+
(SELECT user_id, AVG(activity_duration) AS avg_paid
14+
FROM UserActivity
15+
WHERE activity_type = 'paid'
16+
GROUP BY user_id) pt
17+
ON ft.user_id = pt.user_id
18+
ORDER BY ft.user_id ASC;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package g3401_3500.s3497_analyze_subscription_conversion;
2+
3+
import static org.hamcrest.CoreMatchers.equalTo;
4+
import static org.hamcrest.MatcherAssert.assertThat;
5+
6+
import java.io.BufferedReader;
7+
import java.io.FileNotFoundException;
8+
import java.io.FileReader;
9+
import java.sql.Connection;
10+
import java.sql.ResultSet;
11+
import java.sql.SQLException;
12+
import java.sql.Statement;
13+
import java.util.stream.Collectors;
14+
import javax.sql.DataSource;
15+
import org.junit.jupiter.api.Test;
16+
import org.zapodot.junit.db.annotations.EmbeddedDatabase;
17+
import org.zapodot.junit.db.annotations.EmbeddedDatabaseTest;
18+
import org.zapodot.junit.db.common.CompatibilityMode;
19+
20+
@EmbeddedDatabaseTest(
21+
compatibilityMode = CompatibilityMode.MySQL,
22+
initialSqls =
23+
" CREATE TABLE UserActivity ("
24+
+ " user_id INT,"
25+
+ " activity_date date,"
26+
+ " activity_type VARCHAR(100),"
27+
+ " activity_duration INT"
28+
+ ");"
29+
+ "INSERT INTO UserActivity (user_id, activity_date, activity_type, activity_duration)"
30+
+ "VALUES"
31+
+ " (1, '2023-01-01', 'free_trial', 45),"
32+
+ " (1, '2023-01-02', 'free_trial', 30),"
33+
+ " (1, '2023-01-05', 'free_trial', 60),"
34+
+ " (1, '2023-01-10', 'paid', 75),"
35+
+ " (1, '2023-01-12', 'paid', 90),"
36+
+ " (1, '2023-01-15', 'paid', 65),"
37+
+ " (2, '2023-02-01', 'free_trial', 55),"
38+
+ " (2, '2023-02-03', 'free_trial', 25),"
39+
+ " (2, '2023-02-07', 'free_trial', 50),"
40+
+ " (2, '2023-02-10', 'cancelled', 0),"
41+
+ " (3, '2023-03-05', 'free_trial', 70),"
42+
+ " (3, '2023-03-06', 'free_trial', 60),"
43+
+ " (3, '2023-03-08', 'free_trial', 80),"
44+
+ " (3, '2023-03-12', 'paid', 50),"
45+
+ " (3, '2023-03-15', 'paid', 55),"
46+
+ " (3, '2023-03-20', 'paid', 85),"
47+
+ " (4, '2023-04-01', 'free_trial', 40),"
48+
+ " (4, '2023-04-03', 'free_trial', 35),"
49+
+ " (4, '2023-04-05', 'paid', 45),"
50+
+ " (4, '2023-04-07', 'cancelled', 0);")
51+
class MysqlTest {
52+
@Test
53+
void testScript(@EmbeddedDatabase DataSource dataSource)
54+
throws SQLException, FileNotFoundException {
55+
try (final Connection connection = dataSource.getConnection()) {
56+
try (final Statement statement = connection.createStatement();
57+
final ResultSet resultSet =
58+
statement.executeQuery(
59+
new BufferedReader(
60+
new FileReader(
61+
"src/main/java/g3401_3500/"
62+
+ "s3497_analyze_subscription_conversion/"
63+
+ "script.sql"))
64+
.lines()
65+
.collect(Collectors.joining("\n"))
66+
.replaceAll("#.*?\\r?\\n", ""))) {
67+
checkRow(resultSet, new String[] {"1", "45.0", "76.67"});
68+
checkRow(resultSet, new String[] {"3", "70.0", "63.33"});
69+
checkRow(resultSet, new String[] {"4", "37.5", "45.0"});
70+
assertThat(resultSet.next(), equalTo(false));
71+
}
72+
}
73+
}
74+
75+
private void checkRow(ResultSet resultSet, String[] values) throws SQLException {
76+
assertThat(resultSet.next(), equalTo(true));
77+
assertThat(resultSet.getNString(1), equalTo(values[0]));
78+
assertThat(resultSet.getNString(2), equalTo(values[1]));
79+
assertThat(resultSet.getNString(3), equalTo(values[2]));
80+
}
81+
}

0 commit comments

Comments
 (0)