From 2a9ce14faed346696c72f78102c245671db6e1de Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Wed, 12 Mar 2025 21:55:08 +0200 Subject: [PATCH] Added task 3482 --- .../readme.md | 96 +++++++++++++++++++ .../script.sql | 43 +++++++++ .../MysqlTest.kt | 96 +++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/readme.md create mode 100644 src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/script.sql create mode 100644 src/test/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/MysqlTest.kt diff --git a/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/readme.md b/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/readme.md new file mode 100644 index 000000000..a8080ee4e --- /dev/null +++ b/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/readme.md @@ -0,0 +1,96 @@ +3482\. Analyze Organization Hierarchy + +Hard + +Table: `Employees` + + +----------------+---------+ + | Column Name | Type | + +----------------+---------+ + | employee_id | int | + | employee_name | varchar | + | manager_id | int | + | salary | int | + | department | varchar | + +----------------+---------+ + employee_id is the unique key for this table. + Each row contains information about an employee, including their ID, name, their manager's ID, salary, and department. + manager_id is null for the top-level manager (CEO). + +Write a solution to analyze the organizational hierarchy and answer the following: + +1. **Hierarchy Levels:** For each employee, determine their level in the organization (CEO is level `1`, employees reporting directly to the CEO are level `2`, and so on). +2. **Team Size:** For each employee who is a manager, count the total number of employees under them (direct and indirect reports). +3. **Salary Budget:** For each manager, calculate the total salary budget they control (sum of salaries of all employees under them, including indirect reports, plus their own salary). + +Return _the result table ordered by _the result ordered by **level** in **ascending** order, then by **budget** in **descending** order, and finally by **employee\_name** in **ascending** order_._ + +_The result format is in the following example._ + +**Example:** + +**Input:** + +Employees table: + + +-------------+---------------+------------+--------+-------------+ + | employee_id | employee_name | manager_id | salary | department | + +-------------+---------------+------------+--------+-------------+ + | 1 | Alice | null | 12000 | Executive | + | 2 | Bob | 1 | 10000 | Sales | + | 3 | Charlie | 1 | 10000 | Engineering | + | 4 | David | 2 | 7500 | Sales | + | 5 | Eva | 2 | 7500 | Sales | + | 6 | Frank | 3 | 9000 | Engineering | + | 7 | Grace | 3 | 8500 | Engineering | + | 8 | Hank | 4 | 6000 | Sales | + | 9 | Ivy | 6 | 7000 | Engineering | + | 10 | Judy | 6 | 7000 | Engineering | + +-------------+---------------+------------+--------+-------------+ + +**Output:** + + +-------------+---------------+-------+-----------+--------+ + | employee_id | employee_name | level | team_size | budget | + +-------------+---------------+-------+-----------+--------+ + | 1 | Alice | 1 | 9 | 84500 | + | 3 | Charlie | 2 | 4 | 41500 | + | 2 | Bob | 2 | 3 | 31000 | + | 6 | Frank | 3 | 2 | 23000 | + | 4 | David | 3 | 1 | 13500 | + | 7 | Grace | 3 | 0 | 8500 | + | 5 | Eva | 3 | 0 | 7500 | + | 9 | Ivy | 4 | 0 | 7000 | + | 10 | Judy | 4 | 0 | 7000 | + | 8 | Hank | 4 | 0 | 6000 | + +-------------+---------------+-------+-----------+--------+ + +**Explanation:** + +* **Organization Structure:** + * Alice (ID: 1) is the CEO (level 1) with no manager + * Bob (ID: 2) and Charlie (ID: 3) report directly to Alice (level 2) + * David (ID: 4), Eva (ID: 5) report to Bob, while Frank (ID: 6) and Grace (ID: 7) report to Charlie (level 3) + * Hank (ID: 8) reports to David, and Ivy (ID: 9) and Judy (ID: 10) report to Frank (level 4) +* **Level Calculation:** + * The CEO (Alice) is at level 1 + * Each subsequent level of management adds 1 to the level +* **Team Size Calculation:** + * Alice has 9 employees under her (the entire company except herself) + * Bob has 3 employees (David, Eva, and Hank) + * Charlie has 4 employees (Frank, Grace, Ivy, and Judy) + * David has 1 employee (Hank) + * Frank has 2 employees (Ivy and Judy) + * Eva, Grace, Hank, Ivy, and Judy have no direct reports (team\_size = 0) +* **Budget Calculation:** + * Alice's budget: Her salary (12000) + all employees' salaries (72500) = 84500 + * Charlie's budget: His salary (10000) + Frank's budget (23000) + Grace's salary (8500) = 41500 + * Bob's budget: His salary (10000) + David's budget (13500) + Eva's salary (7500) = 31000 + * Frank's budget: His salary (9000) + Ivy's salary (7000) + Judy's salary (7000) = 23000 + * David's budget: His salary (7500) + Hank's salary (6000) = 13500 + * Employees with no direct reports have budgets equal to their own salary + +**Note:** + +* The result is ordered first by level in ascending order +* Within the same level, employees are ordered by budget in descending order then by name in ascending order \ No newline at end of file diff --git a/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/script.sql b/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/script.sql new file mode 100644 index 000000000..7503ec213 --- /dev/null +++ b/src/main/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/script.sql @@ -0,0 +1,43 @@ +# Write your MySQL query statement below +# #Hard #Database #2025_03_11_Time_712_ms_(100.00%)_Space_0.0_MB_(100.00%) +with recursive org_hierarchy(orig_employee_id, orig_employee_name, employee_id, employee_name, manager_id, salary, org_level) as +( + select employee_id as orig_employee_id, + employee_name as orig_employee_name, + employee_id, + employee_name, + manager_id, + salary, + 1 as org_level + from Employees + UNION ALL + select P.orig_employee_id, + P.orig_employee_name, + CH.employee_id, + CH.employee_name, + CH.manager_id, + CH.salary, + P.org_level + 1 + from org_hierarchy P, Employees CH + where ch.manager_id = P.employee_id +), +CEO_hierarchy as ( + select org_hierarchy.employee_id as SUB_employee_id, + org_hierarchy.employee_name, + org_hierarchy.org_level as sub_level + from org_hierarchy, Employees + where org_hierarchy.orig_employee_id = Employees.employee_id + and Employees.manager_id is null +) +select +org_hierarchy.ORIG_EMPLOYEE_ID as employee_id, +org_hierarchy.ORIG_EMPLOYEE_name as employee_name, +CEO_hierarchy.sub_level as "level", +count(*) - 1 as team_size, +sum(org_hierarchy.salary) as budget +from org_hierarchy, CEO_hierarchy +where org_hierarchy.ORIG_EMPLOYEE_ID = CEO_hierarchy.SUB_employee_id +group by org_hierarchy.ORIG_EMPLOYEE_ID, +org_hierarchy.ORIG_EMPLOYEE_name, +CEO_hierarchy.sub_level +order by 3 asc, 5 desc, 2 diff --git a/src/test/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/MysqlTest.kt b/src/test/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/MysqlTest.kt new file mode 100644 index 000000000..49ba830e6 --- /dev/null +++ b/src/test/kotlin/g3401_3500/s3482_analyze_organization_hierarchy/MysqlTest.kt @@ -0,0 +1,96 @@ +package g3401_3500.s3482_analyze_organization_hierarchy + +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Test +import org.zapodot.junit.db.annotations.EmbeddedDatabase +import org.zapodot.junit.db.annotations.EmbeddedDatabaseTest +import org.zapodot.junit.db.common.CompatibilityMode +import java.io.BufferedReader +import java.io.FileNotFoundException +import java.io.FileReader +import java.sql.ResultSet +import java.sql.SQLException +import java.util.stream.Collectors +import javax.sql.DataSource + +@EmbeddedDatabaseTest( + compatibilityMode = CompatibilityMode.MySQL, + initialSqls = [ + ( + " CREATE TABLE Employees (" + + " employee_id INT," + + " employee_name VARCHAR(50)," + + " manager_id INT," + + " salary INT," + + " department VARCHAR(100)" + + ");" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(1, 'Alice', NULL, 12000, 'Executive');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(2, 'Bob', 1, 10000, 'Sales');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(3, 'Charlie', 1, 10000, 'Engineering');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(4, 'David', 2, 7500, 'Sales');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(5, 'Eva', 2, 7500, 'Sales');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(6, 'Frank', 3, 9000, 'Engineering');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(7, 'Grace', 3, 8500, 'Engineering');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(8, 'Hank', 4, 6000, 'Sales');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(9, 'Ivy', 6, 7000, 'Engineering');" + + "insert into Employees (employee_id, employee_name, manager_id, salary, department) values " + + "(10, 'Judy', 6, 7000, 'Engineering');" + ), + ], +) +internal class MysqlTest { + @Test + @Throws(SQLException::class, FileNotFoundException::class) + fun testScript(@EmbeddedDatabase dataSource: DataSource) { + dataSource.connection.use { connection -> + connection.createStatement().use { statement -> + statement.executeQuery( + BufferedReader( + FileReader( + ( + "src/main/kotlin/g3401_3500/" + + "s3482_analyze_organization_hierarchy/" + + "script.sql" + ), + ), + ) + .lines() + .collect(Collectors.joining("\n")) + .replace("#.*?\\r?\\n".toRegex(), ""), + ).use { resultSet -> + checkRow(resultSet, arrayOf("1", "Alice", "1", "9", "84500")) + checkRow(resultSet, arrayOf("3", "Charlie", "2", "4", "41500")) + checkRow(resultSet, arrayOf("2", "Bob", "2", "3", "31000")) + checkRow(resultSet, arrayOf("6", "Frank", "3", "2", "23000")) + checkRow(resultSet, arrayOf("4", "David", "3", "1", "13500")) + checkRow(resultSet, arrayOf("7", "Grace", "3", "0", "8500")) + checkRow(resultSet, arrayOf("5", "Eva", "3", "0", "7500")) + checkRow(resultSet, arrayOf("9", "Ivy", "4", "0", "7000")) + checkRow(resultSet, arrayOf("10", "Judy", "4", "0", "7000")) + checkRow(resultSet, arrayOf("8", "Hank", "4", "0", "6000")) + assertThat(resultSet.next(), equalTo(false)) + } + } + } + } + + @Throws(SQLException::class) + private fun checkRow(resultSet: ResultSet, values: Array) { + assertThat(resultSet.next(), equalTo(true)) + assertThat(resultSet.getNString(1), equalTo(values[0])) + assertThat(resultSet.getNString(2), equalTo(values[1])) + assertThat(resultSet.getNString(3), equalTo(values[2])) + assertThat(resultSet.getNString(4), equalTo(values[3])) + assertThat(resultSet.getNString(5), equalTo(values[4])) + } +}