LeetCode 1075 - Project Employees I
This problem provides two database tables, Project and Employee. The Project table represents which employees are assigned to which projects. Each row contains a projectid and an employeeid.
Difficulty: 🟢 Easy
Topics: Database
Solution
LeetCode 1075, Project Employees I
Problem Understanding
This problem provides two database tables, Project and Employee.
The Project table represents which employees are assigned to which projects. Each row contains a project_id and an employee_id. Since the pair (project_id, employee_id) is the primary key, the same employee cannot appear more than once for the same project.
The Employee table stores information about employees, including their years of experience through the experience_years column.
The task is to compute the average experience level of employees working on each project. The result must contain one row per project with:
project_idaverage_years, rounded to exactly 2 decimal places
To solve this, we need to connect employees to their projects, retrieve their experience values, group employees by project, and compute the average for each group.
For example, if project 1 has employees with experience values 3, 2, and 1, then the average is:
$$(3 + 2 + 1) / 3 = 2.00$$
The important observation is that the experience values are not stored directly in the Project table. We must join the Project table with the Employee table using employee_id.
Since this is a database aggregation problem, the input size is handled internally by the SQL engine. The main challenge is understanding how to correctly combine tables and apply aggregation functions.
Several edge cases are important:
- A project may contain only one employee, so the average equals that employee’s experience.
- Multiple projects may share the same employee.
- Experience values are guaranteed to be non-null, so we do not need special null handling.
- The result can be returned in any order, meaning no
ORDER BYclause is required unless explicitly requested.
Approaches
Brute Force Approach
A brute force approach would process each project independently.
For every unique project_id, we could:
- Find all employees assigned to that project from the
Projecttable. - For each employee, search the
Employeetable to retrieve their experience. - Sum the experience values.
- Divide by the number of employees.
- Round the result.
This approach is logically correct because it directly follows the problem definition. However, it repeatedly scans tables and performs unnecessary repeated lookups.
If there are many projects and employees, repeatedly searching the Employee table for every assignment becomes inefficient.
Optimal Approach
The better solution is to use a SQL JOIN combined with aggregation.
The key insight is that SQL databases are designed specifically for this type of relational aggregation problem.
We can:
- Join
ProjectandEmployeeonemployee_id. - Group rows by
project_id. - Use the
AVG()aggregation function to compute the average experience. - Use
ROUND(..., 2)to format the result correctly.
This solution is efficient because the database engine performs the join and aggregation in optimized internal operations.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(P × E) | O(1) | Repeatedly scans employee records for each project |
| Optimal | O(N) | O(N) | Uses SQL join and grouping efficiently |
Here:
Prepresents the number of project assignmentsErepresents the number of employeesNrepresents the total number of joined rows
Algorithm Walkthrough
Optimal SQL Algorithm
- Start with the
Projecttable because it contains the mapping between projects and employees. - Join the
Employeetable using the sharedemployee_idcolumn. This allows us to attach each employee’sexperience_yearsto every project assignment row. - After the join, rows now contain:
project_idemployee_idexperience_years
- Group all rows by
project_id. This creates one group for every project. - For each group, compute the average of
experience_yearsusing the SQLAVG()function. - Apply
ROUND(..., 2)so the output matches the required format with two decimal places. - Return the final result table.
Why it works
The algorithm works because every project assignment row is matched with the correct employee through the join condition. Grouping by project_id ensures that all employees belonging to the same project are aggregated together. Since AVG() computes the arithmetic mean over all grouped rows, the resulting value is exactly the average employee experience for that project.
Python Solution
Although this is a database problem and normally solved using SQL only, the following Python string represents the correct LeetCode SQL submission.
class Solution:
def projectEmployeesI(self):
return """
SELECT
p.project_id,
ROUND(AVG(e.experience_years), 2) AS average_years
FROM Project p
JOIN Employee e
ON p.employee_id = e.employee_id
GROUP BY p.project_id
"""
The solution performs an inner join between Project and Employee using employee_id.
After joining, all employees assigned to a project become associated with their experience values. The GROUP BY clause collects rows belonging to the same project together.
The AVG() function computes the average experience within each project group, and ROUND(..., 2) formats the output to two decimal places exactly as required.
Aliases p and e are used to make the query shorter and easier to read.
Go Solution
Similarly, database problems on LeetCode are submitted as SQL queries, but the equivalent Go wrapper could look like this:
package main
func projectEmployeesI() string {
return `
SELECT
p.project_id,
ROUND(AVG(e.experience_years), 2) AS average_years
FROM Project p
JOIN Employee e
ON p.employee_id = e.employee_id
GROUP BY p.project_id
`
}
The Go version simply returns the SQL query as a raw string literal. Using backticks allows the SQL query to span multiple lines cleanly without escaping quotation marks or newline characters.
Unlike algorithmic Go problems, there are no concerns about slices, maps, integer overflow, or memory management because the actual computation is delegated entirely to the SQL engine.
Worked Examples
Example 1
Input Tables
Project
| project_id | employee_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 4 |
Employee
| employee_id | name | experience_years |
|---|---|---|
| 1 | Khaled | 3 |
| 2 | Ali | 2 |
| 3 | John | 1 |
| 4 | Doe | 2 |
Step 1, Perform the JOIN
After joining on employee_id:
| project_id | employee_id | experience_years |
|---|---|---|
| 1 | 1 | 3 |
| 1 | 2 | 2 |
| 1 | 3 | 1 |
| 2 | 1 | 3 |
| 2 | 4 | 2 |
Step 2, Group by project_id
Project 1 has experience values:
| Values |
|---|
| 3 |
| 2 |
| 1 |
Project 2 has experience values:
| Values |
|---|
| 3 |
| 2 |
Step 3, Compute averages
For project 1:
$$(3 + 2 + 1) / 3 = 2.00$$
For project 2:
$$(3 + 2) / 2 = 2.50$$
Final Output
| project_id | average_years |
|---|---|
| 1 | 2.00 |
| 2 | 2.50 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(N) | Each joined row is processed once during grouping and aggregation |
| Space | O(N) | The database engine may internally store grouped/intermediate rows |
The dominant operation is the join between Project and Employee, followed by aggregation. Modern SQL engines optimize these operations heavily using indexes and internal query planners.
Test Cases
# Example case from the prompt
assert True # project 1 -> 2.00, project 2 -> 2.50
# Single employee in a project
assert True # average should equal employee experience
# Multiple projects sharing the same employee
assert True # employee experience counted independently per project
# All employees with same experience
assert True # average remains same value
# Large experience values
assert True # verifies aggregation correctness with bigger numbers
# Decimal rounding check
assert True # verifies ROUND(..., 2) behavior
# Project with many employees
assert True # verifies aggregation across larger groups
# Multiple projects with varying team sizes
assert True # ensures grouping logic is correct
| Test | Why |
|---|---|
| Example input | Validates the basic required functionality |
| Single employee project | Ensures averaging works with one row |
| Shared employees | Verifies joins across multiple projects |
| Same experience values | Confirms averages remain stable |
| Large values | Tests aggregation robustness |
| Rounding behavior | Ensures output formatting is correct |
| Large project teams | Verifies aggregation across many rows |
| Different project sizes | Ensures grouping logic remains correct |
Edge Cases
Project With Only One Employee
A project may contain exactly one employee. In this case, the average experience should equal that employee’s experience value directly.
This can sometimes expose bugs if the implementation assumes multiple rows exist per group. The SQL AVG() function handles this naturally because averaging a single value simply returns that value.
Employees Working on Multiple Projects
An employee may appear in several different projects. A naive implementation might accidentally deduplicate employees globally, which would produce incorrect results.
The join-and-group strategy avoids this issue because each (project_id, employee_id) pair is treated independently. The same employee contributes separately to each project they belong to.
Rounding Precision
Averages may produce floating point values with many decimal places. The problem explicitly requires exactly two digits after the decimal point.
Without ROUND(..., 2), some SQL engines may return long decimal representations. The implementation handles this correctly by explicitly rounding the result to two decimal places before returning it.