Skip to content

Commit c931341

Browse files
authored
Added task 3626
1 parent 6b5276a commit c931341

File tree

3 files changed

+302
-0
lines changed
  • src
    • main/kotlin/g3601_3700/s3626_find_stores_with_inventory_imbalance
    • test/kotlin/g3601_3700/s3626_find_stores_with_inventory_imbalance

3 files changed

+302
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
3626\. Find Stores with Inventory Imbalance
2+
3+
Medium
4+
5+
Table: `stores`
6+
7+
+------------+----------+
8+
| Column Name| Type |
9+
+------------+----------+
10+
| store_id | int |
11+
| store_name | varchar |
12+
| location | varchar |
13+
+------------+----------+
14+
15+
store_id is the unique identifier for this table.
16+
Each row contains information about a store and its location.
17+
18+
Table: `inventory`
19+
20+
+--------------+----------+
21+
| Column Name | Type |
22+
+--------------+----------+
23+
| inventory_id | int |
24+
| store_id | int |
25+
| product_name | varchar |
26+
| quantity | int |
27+
| price | decimal |
28+
+--------------+----------+
29+
30+
inventory_id is the unique identifier for this table.
31+
Each row represents the inventory of a specific product at a specific store.
32+
33+
Write a solution to find stores that have **inventory imbalance** - stores where the most expensive product has lower stock than the cheapest product.
34+
35+
* For each store, identify the **most expensive product** (highest price) and its quantity
36+
* For each store, identify the **cheapest product** (lowest price) and its quantity
37+
* A store has inventory imbalance if the most expensive product's quantity is **less than** the cheapest product's quantity
38+
* Calculate the **imbalance ratio** as (cheapest\_quantity / most\_expensive\_quantity)
39+
* **Round** the imbalance ratio to **2** decimal places
40+
* Only include stores that have **at least** `3` **different products**
41+
42+
Return _the result table ordered by imbalance ratio in **descending** order, then by store name in **ascending** order_.
43+
44+
The result format is in the following example.
45+
46+
**Example:**
47+
48+
**Input:**
49+
50+
stores table:
51+
52+
+----------+----------------+-------------+
53+
| store_id | store_name | location |
54+
+----------+----------------+-------------+
55+
| 1 | Downtown Tech | New York |
56+
| 2 | Suburb Mall | Chicago |
57+
| 3 | City Center | Los Angeles |
58+
| 4 | Corner Shop | Miami |
59+
| 5 | Plaza Store | Seattle |
60+
+----------+----------------+-------------+
61+
62+
inventory table:
63+
64+
+--------------+----------+--------------+----------+--------+
65+
| inventory_id | store_id | product_name | quantity | price |
66+
+--------------+----------+--------------+----------+--------+
67+
| 1 | 1 | Laptop | 5 | 999.99 |
68+
| 2 | 1 | Mouse | 50 | 19.99 |
69+
| 3 | 1 | Keyboard | 25 | 79.99 |
70+
| 4 | 1 | Monitor | 15 | 299.99 |
71+
| 5 | 2 | Phone | 3 | 699.99 |
72+
| 6 | 2 | Charger | 100 | 25.99 |
73+
| 7 | 2 | Case | 75 | 15.99 |
74+
| 8 | 2 | Headphones | 20 | 149.99 |
75+
| 9 | 3 | Tablet | 2 | 499.99 |
76+
| 10 | 3 | Stylus | 80 | 29.99 |
77+
| 11 | 3 | Cover | 60 | 39.99 |
78+
| 12 | 4 | Watch | 10 | 299.99 |
79+
| 13 | 4 | Band | 25 | 49.99 |
80+
| 14 | 5 | Camera | 8 | 599.99 |
81+
| 15 | 5 | Lens | 12 | 199.99 |
82+
+--------------+----------+--------------+----------+--------+
83+
84+
**Output:**
85+
86+
+----------+----------------+-------------+------------------+--------------------+------------------+
87+
| store_id | store_name | location | most_exp_product | cheapest_product | imbalance_ratio |
88+
+----------+----------------+-------------+------------------+--------------------+------------------+
89+
| 3 | City Center | Los Angeles | Tablet | Stylus | 40.00 |
90+
| 1 | Downtown Tech | New York | Laptop | Mouse | 10.00 |
91+
| 2 | Suburb Mall | Chicago | Phone | Case | 25.00 |
92+
+----------+----------------+-------------+------------------+--------------------+------------------+
93+
94+
**Explanation:**
95+
96+
* **Downtown Tech (store\_id = 1):**
97+
* Most expensive product: Laptop ($999.99) with quantity 5
98+
* Cheapest product: Mouse ($19.99) with quantity 50
99+
* Inventory imbalance: 5 < 50 (expensive product has lower stock)
100+
* Imbalance ratio: 50 / 5 = 10.00
101+
* Has 4 products (≥ 3), so qualifies
102+
* **Suburb Mall (store\_id = 2):**
103+
* Most expensive product: Phone ($699.99) with quantity 3
104+
* Cheapest product: Case ($15.99) with quantity 75
105+
* Inventory imbalance: 3 < 75 (expensive product has lower stock)
106+
* Imbalance ratio: 75 / 3 = 25.00
107+
* Has 4 products (≥ 3), so qualifies
108+
* **City Center (store\_id = 3):**
109+
* Most expensive product: Tablet ($499.99) with quantity 2
110+
* Cheapest product: Stylus ($29.99) with quantity 80
111+
* Inventory imbalance: 2 < 80 (expensive product has lower stock)
112+
* Imbalance ratio: 80 / 2 = 40.00
113+
* Has 3 products (≥ 3), so qualifies
114+
* **Stores not included:**
115+
* Corner Shop (store\_id = 4): Only has 2 products (Watch, Band) - doesn't meet minimum 3 products requirement
116+
* Plaza Store (store\_id = 5): Only has 2 products (Camera, Lens) - doesn't meet minimum 3 products requirement
117+
118+
The Results table is ordered by imbalance ratio in descending order, then by store name in ascending order
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Write your MySQL query statement below
2+
# #Medium #2025_07_25_Time_516_ms_(100.00%)_Space_0.0_MB_(100.00%)
3+
WITH store_product_check AS (
4+
SELECT
5+
s.store_id,
6+
s.store_name,
7+
s.location,
8+
COUNT(i.inventory_id) AS store_product_ct
9+
FROM
10+
stores s
11+
JOIN inventory i ON s.store_id = i.store_id
12+
GROUP BY
13+
s.store_id,
14+
s.store_name,
15+
s.location
16+
HAVING
17+
COUNT(i.inventory_id) >= 3
18+
),
19+
store_product_ranked AS (
20+
SELECT
21+
s.store_id,
22+
s.store_name,
23+
s.location,
24+
i.inventory_id,
25+
i.product_name,
26+
i.quantity,
27+
i.price,
28+
ROW_NUMBER() OVER (PARTITION BY s.store_id ORDER BY i.price ASC) AS low_price_rk,
29+
ROW_NUMBER() OVER (PARTITION BY s.store_id ORDER BY i.price DESC) AS high_price_rk
30+
FROM
31+
stores s
32+
JOIN inventory i ON s.store_id = i.store_id
33+
),
34+
high_low_price AS (
35+
SELECT
36+
spc.store_id,
37+
spc.store_name,
38+
spc.location,
39+
lp.product_name AS low_price_product_name,
40+
lp.quantity + 0.0 AS low_price_quantity,
41+
hp.product_name AS high_price_product_name,
42+
hp.quantity + 0.0 AS high_price_quantity
43+
FROM
44+
store_product_check spc
45+
JOIN store_product_ranked lp
46+
ON spc.store_id = lp.store_id AND lp.low_price_rk = 1
47+
JOIN store_product_ranked hp
48+
ON spc.store_id = hp.store_id AND hp.high_price_rk = 1
49+
)
50+
SELECT
51+
hlp.store_id,
52+
hlp.store_name,
53+
hlp.location,
54+
hlp.high_price_product_name AS most_exp_product,
55+
hlp.low_price_product_name AS cheapest_product,
56+
ROUND(hlp.low_price_quantity / hlp.high_price_quantity, 2) AS imbalance_ratio
57+
FROM
58+
high_low_price hlp
59+
WHERE
60+
hlp.high_price_quantity < hlp.low_price_quantity
61+
ORDER BY
62+
imbalance_ratio DESC,
63+
hlp.store_name ASC;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package g3601_3700.s3626_find_stores_with_inventory_imbalance
2+
3+
import org.hamcrest.CoreMatchers.equalTo
4+
import org.hamcrest.MatcherAssert.assertThat
5+
import org.junit.jupiter.api.Test
6+
import org.zapodot.junit.db.annotations.EmbeddedDatabase
7+
import org.zapodot.junit.db.annotations.EmbeddedDatabaseTest
8+
import org.zapodot.junit.db.common.CompatibilityMode
9+
import java.io.BufferedReader
10+
import java.io.FileNotFoundException
11+
import java.io.FileReader
12+
import java.sql.SQLException
13+
import java.util.stream.Collectors
14+
import javax.sql.DataSource
15+
16+
@EmbeddedDatabaseTest(
17+
compatibilityMode = CompatibilityMode.MySQL,
18+
initialSqls = [
19+
(
20+
"CREATE TABLE stores (" +
21+
" store_id INT PRIMARY KEY," +
22+
" store_name VARCHAR(50)," +
23+
" location VARCHAR(50)" +
24+
");" +
25+
"INSERT INTO stores (store_id, store_name, location) VALUES" +
26+
"(1, 'Downtown Tech', 'New York')," +
27+
"(2, 'Suburb Mall', 'Chicago')," +
28+
"(3, 'City Center', 'Los Angeles')," +
29+
"(4, 'Corner Shop', 'Miami')," +
30+
"(5, 'Plaza Store', 'Seattle');" +
31+
"CREATE TABLE inventory (" +
32+
" inventory_id INT PRIMARY KEY," +
33+
" store_id INT," +
34+
" product_name VARCHAR(50)," +
35+
" quantity INT," +
36+
" price DECIMAL(10,2)," +
37+
" FOREIGN KEY (store_id) REFERENCES stores(store_id)" +
38+
");" +
39+
"INSERT INTO inventory (inventory_id, store_id, " +
40+
"product_name, quantity, price) VALUES" +
41+
"(1, 1, 'Laptop', 5, 999.99)," +
42+
"(2, 1, 'Mouse', 50, 19.99)," +
43+
"(3, 1, 'Keyboard', 25, 79.99)," +
44+
"(4, 1, 'Monitor', 15, 299.99)," +
45+
"(5, 2, 'Phone', 3, 699.99)," +
46+
"(6, 2, 'Charger', 100, 25.99)," +
47+
"(7, 2, 'Case', 75, 15.99)," +
48+
"(8, 2, 'Headphones', 20, 149.99)," +
49+
"(9, 3, 'Tablet', 2, 499.99)," +
50+
"(10, 3, 'Stylus', 80, 29.99)," +
51+
"(11, 3, 'Cover', 60, 39.99)," +
52+
"(12, 4, 'Watch', 10, 299.99)," +
53+
"(13, 4, 'Band', 25, 49.99)," +
54+
"(14, 5, 'Camera', 8, 599.99)," +
55+
"(15, 5, 'Lens', 12, 199.99);"
56+
),
57+
],
58+
)
59+
internal class MysqlTest {
60+
@Test
61+
@Throws(SQLException::class, FileNotFoundException::class)
62+
fun testScript(@EmbeddedDatabase dataSource: DataSource) {
63+
dataSource.connection.use { connection ->
64+
connection.createStatement().use { statement ->
65+
statement.executeQuery(
66+
BufferedReader(
67+
FileReader(
68+
(
69+
"src/main/kotlin/g3601_3700/" +
70+
"s3626_find_stores_with_" +
71+
"inventory_imbalance/" +
72+
"script.sql"
73+
),
74+
),
75+
)
76+
.lines()
77+
.collect(Collectors.joining("\n"))
78+
.replace("#.*?\\r?\\n".toRegex(), ""),
79+
).use { resultSet ->
80+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(true))
81+
assertThat<String>(resultSet.getNString(1), equalTo<String>("3"))
82+
assertThat<String>(
83+
resultSet.getNString(2),
84+
equalTo<String>("City Center"),
85+
)
86+
assertThat<String>(
87+
resultSet.getNString(3),
88+
equalTo<String>("Los Angeles"),
89+
)
90+
assertThat<String>(resultSet.getNString(4), equalTo<String>("Tablet"))
91+
assertThat<String>(resultSet.getNString(5), equalTo<String>("Stylus"))
92+
assertThat<String>(resultSet.getNString(6), equalTo<String>("40.00"))
93+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(true))
94+
assertThat<String>(resultSet.getNString(1), equalTo<String>("2"))
95+
assertThat<String>(
96+
resultSet.getNString(2),
97+
equalTo<String>("Suburb Mall"),
98+
)
99+
assertThat<String>(resultSet.getNString(3), equalTo<String>("Chicago"))
100+
assertThat<String>(resultSet.getNString(4), equalTo<String>("Phone"))
101+
assertThat<String>(resultSet.getNString(5), equalTo<String>("Case"))
102+
assertThat<String>(resultSet.getNString(6), equalTo<String>("25.00"))
103+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(true))
104+
assertThat<String>(resultSet.getNString(1), equalTo<String>("1"))
105+
assertThat<String>(
106+
resultSet.getNString(2),
107+
equalTo<String>("Downtown Tech"),
108+
)
109+
assertThat<String>(
110+
resultSet.getNString(3),
111+
equalTo<String>("New York"),
112+
)
113+
assertThat<String>(resultSet.getNString(4), equalTo<String>("Laptop"))
114+
assertThat<String>(resultSet.getNString(5), equalTo<String>("Mouse"))
115+
assertThat<String>(resultSet.getNString(6), equalTo<String>("10.00"))
116+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(false))
117+
}
118+
}
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)