SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

Masar Holding HR

Masar Holding (fictional) · 10 tables · 5,684 rows · version 1

Open in the playground

Masar Holding is a fictional Saudi group with head-office functions, a logistics operation, sales teams in three cities and a customer service centre. The dataset holds its departments (a hierarchy), locations, jobs and salary bands, every employee who has worked there since 2015 (including leavers and rehires) with their manager, the full salary history, annual performance reviews for 2023–2025, leave requests for 2024–2025 and incentive payments. Use it for headcount and turnover at a date, self-joins and recursive org charts, salary-band compliance, window functions over salary history, and workforce analytics.

The documentation is open to everyone. Querying this dataset in the playground is included with Formula Quest membership; lessons that use it follow their own access.

Relationships

locations6PKlocation_idlocation_namecityregionis_head_officedepartments16PKdepartment_idFKparent_department_iddepartment_namecost_center_codeestablished_onjobs37PKjob_idjob_titlejob_familygradesalary_bands30PKgradePKeffective_frommin_salarymid_salarymax_salaryemployees490PKemployee_idFKdepartment_idFKjob_idFKlocation_idFKmanager_idfirst_namelast_nameemailgender+ 6 more columnssalary_history1,742PKsalary_idFKemployee_idFKjob_ideffective_datebase_salaryhousing_allowancetransport_allowancereasonperformance_reviews770PKreview_idFKemployee_idFKreviewer_idreview_yearratingreviewed_onleave_requests1,592PKleave_idFKemployee_idleave_typestart_dateend_datedaysstatusrequested_onincentive_payments966PKpayment_idFKemployee_idplanperformance_yearpaid_onamountdepartment_transfers35PKtransfer_idFKemployee_idFKfrom_department_idFKto_department_ideffective_date
Arrows point from a foreign key to the table it references. PK = primary key, FK = foreign key. Numbers are row counts.

Sample queries

Headcount by department on 31 December 2025
SELECT d.department_name, COUNT(e.employee_id) AS headcount
FROM departments AS d
LEFT JOIN employees AS e
  ON e.department_id = d.department_id
 AND e.hire_date <= DATE '2025-12-31'
 AND (e.termination_date IS NULL OR e.termination_date >= DATE '2025-12-31')
GROUP BY d.department_name
ORDER BY headcount DESC;
Current salary of every active employee
SELECT DISTINCT ON (s.employee_id)
       e.employee_id, e.first_name, e.last_name, s.effective_date, s.base_salary,
       s.base_salary + s.housing_allowance + s.transport_allowance AS total_monthly
FROM salary_history AS s
JOIN employees AS e ON e.employee_id = s.employee_id
WHERE e.termination_date IS NULL
ORDER BY s.employee_id, s.effective_date DESC;
Managers and their number of direct reports
SELECT m.employee_id, m.first_name || ' ' || m.last_name AS manager, COUNT(r.employee_id) AS direct_reports
FROM employees AS m
JOIN employees AS r ON r.manager_id = m.employee_id AND r.termination_date IS NULL
WHERE m.termination_date IS NULL
GROUP BY m.employee_id, manager
ORDER BY direct_reports DESC;
Org chart below the CEO (recursive)
WITH RECURSIVE chart AS (
  SELECT employee_id, manager_id, first_name || ' ' || last_name AS name, 1 AS level
  FROM employees WHERE manager_id IS NULL
  UNION ALL
  SELECT e.employee_id, e.manager_id, e.first_name || ' ' || e.last_name, c.level + 1
  FROM employees e JOIN chart c ON e.manager_id = c.employee_id
  WHERE e.termination_date IS NULL
)
SELECT level, COUNT(*) AS people FROM chart GROUP BY level ORDER BY level;
Leavers per year and reason
SELECT EXTRACT(YEAR FROM termination_date) AS year, termination_reason, COUNT(*) AS leavers
FROM employees
WHERE termination_date IS NOT NULL
GROUP BY 1, 2
ORDER BY 1, 3 DESC;

Data dictionary

locations

6 rows · primary key (location_id)

Offices and sites where employees are based.

ColumnTypeNull?KeyDescription
location_idintegerno PKUnique number for each location.
location_nametextnouniqueName of the office or site.
citytextyesCity. NULL for the Remote location.
regiontextyesRegion of Saudi Arabia. NULL for the Remote location.
is_head_officebooleannoTRUE for the head office.

departments

16 rows · primary key (department_id)

Departments as a hierarchy: each department may belong to a parent department.

ColumnTypeNull?KeyDescription
department_idintegerno PKUnique number for each department.
department_nametextnouniqueDepartment name.
parent_department_idintegeryes departments.department_idThe department this one belongs to. NULL for the top (Executive Office).
cost_center_codetextnouniqueFinance cost center the department's costs are charged to.
established_ondatenoDate the department was created.

jobs

37 rows · primary key (job_id)

Job titles, their job family and pay grade.

ColumnTypeNull?KeyDescription
job_idintegerno PKUnique number for each job.
job_titletextnouniqueJob title.
job_familytextnoGroup of related jobs, e.g. Finance, Sales, Operations.
gradesmallintnoPay grade, 1 (entry) to 10 (CEO). Links to salary_bands.

salary_bands

30 rows · primary key (grade, effective_from)

Monthly base-salary ranges per grade. Bands are revised from time to time; each version applies from effective_from until the next one.

ColumnTypeNull?KeyDescription
gradesmallintno PKPay grade.
effective_fromdateno PKDate this version of the band takes effect.
min_salarynumeric(12,2)noMinimum monthly base salary in SAR (full-time).
mid_salarynumeric(12,2)noMid-point monthly base salary in SAR.
max_salarynumeric(12,2)noMaximum monthly base salary in SAR.

employees

490 rows · primary key (employee_id)

Everyone who has worked for Masar Holding since 2015, including leavers. A rehired person has a new row (new employee_id) with the same email.

ColumnTypeNull?KeyDescription
employee_idintegerno PKUnique number for each employment record (assigned in order of hiring).
first_nametextnoGiven name.
last_nametextnoFamily name.
emailtextnoWork email. Not unique: a rehired person keeps the same address.
gendertextno'F' or 'M'.
nationality_grouptextno'Saudi' or 'Non-Saudi' (used for Saudization reporting).
date_of_birthdatenoDate of birth.
hire_datedatenoFirst working day.
termination_datedateyesLast working day. NULL for current employees.
termination_reasontextyesWhy the employee left. NULL for current employees.
employment_typetextnofull_time, part_time (paid pro rata) or contractor (no allowances, reviews or bonuses).
department_idintegerno departments.department_idCurrent department (the last one, for leavers). Earlier moves are in department_transfers.
job_idintegerno jobs.job_idCurrent job (the last one, for leavers).
location_idintegerno locations.location_idWhere the employee is based.
manager_idintegeryes employees.employee_idLine manager (another employee). NULL only for the CEO. For leavers: the manager when they left.

salary_history

1,742 rows · primary key (salary_id)

Every change to an employee's monthly pay, from hiring onwards. The row with the latest effective_date is the current pay.

ColumnTypeNull?KeyDescription
salary_idintegerno PKUnique number for each salary record.
employee_idintegerno employees.employee_idEmployee.
effective_datedatenoDate the new pay takes effect.
base_salarynumeric(12,2)noMonthly base salary in SAR.
housing_allowancenumeric(12,2)noMonthly housing allowance in SAR (25% of base; 0 for contractors).
transport_allowancenumeric(12,2)noMonthly transport allowance in SAR (10% of base, capped at 3,000; 0 for contractors).
job_idintegerno jobs.job_idJob held from this date (changes on promotion).
reasontextnoWhy pay changed: hire, promotion, annual_increase (each April; none in 2020) or adjustment.

performance_reviews

770 rows · primary key (review_id)

Year-end performance reviews (2023-2025) for employees and part-timers employed on 31 December. Contractors are not reviewed.

ColumnTypeNull?KeyDescription
review_idintegerno PKUnique number for each review.
employee_idintegerno employees.employee_idEmployee reviewed.
review_yearintegernoYear the review covers.
ratingsmallintyes1 (unsatisfactory) to 5 (outstanding). NULL if the employee joined after 1 October or the review is still pending.
reviewer_idintegeryes employees.employee_idManager who did the review. NULL for the CEO (reviewed by the board).
reviewed_ondateyesDate the review was completed. NULL when there is no rating.

leave_requests

1,592 rows · primary key (leave_id)

Leave requests for 2024 and 2025. Only approved requests are leave actually taken.

ColumnTypeNull?KeyDescription
leave_idintegerno PKUnique number for each request.
employee_idintegerno employees.employee_idEmployee requesting leave.
leave_typetextnoannual, sick, unpaid or maternity.
start_datedatenoFirst day of leave.
end_datedatenoLast day of leave.
daysintegernoWorking days of leave (Sunday-Thursday) between start_date and end_date.
statustextnoapproved, rejected or pending (awaiting the manager).
requested_ondatenoDate the request was submitted (sick leave is filed on the first day).

incentive_payments

966 rows · primary key (payment_id)

Bonuses, sales commissions and spot awards paid to employees (2024-2025 payments, plus 2023 spot awards and commissions).

ColumnTypeNull?KeyDescription
payment_idintegerno PKUnique number for each payment.
employee_idintegerno employees.employee_idEmployee paid.
plantextnoannual_bonus (paid in March for the previous year), sales_commission (quarterly, paid the month after the quarter) or spot_award.
performance_yearintegernoYear the payment rewards (can differ from the year it was paid).
paid_ondatenoPayment date.
amountnumeric(12,2)noAmount paid in SAR.

department_transfers

35 rows · primary key (transfer_id)

Moves of employees between departments. employees.department_id holds the department after the latest move.

ColumnTypeNull?KeyDescription
transfer_idintegerno PKUnique number for each transfer.
employee_idintegerno employees.employee_idEmployee who moved.
effective_datedatenoFirst day in the new department.
from_department_idintegerno departments.department_idDepartment before the move.
to_department_idintegerno departments.department_idDepartment after the move.