EntityModel.comDocs
Contents

Start here

Guides

Examples

Platforms

Compare

Reference

Learn

Human resources (HR) data model

This example models the core of an HR system: employees and who they report to, the organization (departments, jobs, positions and work locations), each employee's history of positions and salaries, time off and performance reviews.

Design notes

  • The reporting line is a self-relationship. employee.manager_id points at another employee; the Reporting Line hierarchy turns it into an org chart.
  • Jobs and positions are different things. A job is a definition with a pay grade ("Software Engineer II"); a position is a budgeted seat in a department for a job, and it can be vacant.
  • History is kept, not overwritten. employment_history records each position held, and salary has effective dates, with exactly one open row per employee.
  • Sensitive data is flagged as PII: names, contact details, date of birth, salary and review comments, so it can be masked or access-controlled.
  • Leave requests record who approved them, a second relationship to employee.

To extend it: add benefit_enrollment, payroll_run and payslip, training_course and certification, or job_application for recruiting.

Diagram

Human Resources (HR): 9 entities, 3 reference lists and 14 relationships. Each box is an entity with its attributes (PK marks the primary key); solid lines are relationships in crow's foot notation, dashed lines point to reference lists.

Employee Reports To Employee (N:1)Reports ToEmployee Member Of Department (N:1)Member OfEmployee Holds Position (N:1)HoldsEmployee Works At Work Location (N:1)Works AtDepartment Headed By Employee (N:1)Headed ByPosition In Department (N:1)InPosition Of Job Job (N:1)Of JobEmployment History History Of Employee (N:1)History OfEmployment History In Position Position (N:1)In PositionSalary Paid To Employee (N:1)Paid ToLeave Request Requested By Employee (N:1)Requested ByLeave Request Approved By Employee (N:1)Approved ByPerformance Review Review Of Employee (N:1)Review OfPerformance Review Reviewed By Employee (N:1)Reviewed ByEmployee: A person employed by the company. Managers are employees too.EmployeeemployeePKemployee_idINTemployee_numberSTRINGfull_nameSTRINGwork_emailSTRINGdate_of_birthDATEhire_dateDATEtermination_dateDATEdepartment_idINTposition_idINTmanager_idINTemployment_type_code→ employment_typework_location_idINTDepartment: An organizational unit, such as Finance or Engineering.DepartmentdepartmentPKdepartment_idINTdepartment_nameSTRINGcost_centerSTRINGhead_employee_idINTJob: A job definition with a pay grade, such as Software Engineer II.JobjobPKjob_idINTjob_titleSTRINGjob_familySTRINGpay_gradeSTRINGmin_salaryDECIMALmax_salaryDECIMALPosition: A budgeted seat in a department for a job. A position can be vacant.PositionpositionPKposition_idINTposition_codeSTRINGdepartment_idINTjob_idINTis_vacantBOOLEANWork Location: An office or site where employees work.Work Locationwork_locationPKwork_location_idINTlocation_nameSTRINGcitySTRINGcountry_code→ countryEmployment History: Each position an employee has held, for promotions and transfers.Employment Historyemployment_historyPKhistory_idBIGINTemployee_idINTposition_idINTstart_dateDATEend_dateDATEchange_reasonSTRINGSalary: Base salary over time. Exactly one row per employee has no end date.SalarysalaryPKsalary_idBIGINTemployee_idINTannual_amountDECIMALcurrency_codeSTRINGeffective_fromDATEeffective_toDATELeave Request: A request for time off, approved by the employee's manager.Leave Requestleave_requestPKleave_request_idBIGINTemployee_idINTleave_type_code→ leave_typestart_dateDATEend_dateDATEstatusSTRINGapprover_idINTPerformance Review: A periodic review of an employee by a reviewer.Performance Reviewperformance_reviewPKreview_idBIGINTemployee_idINTreviewer_idINTreview_periodSTRINGratingINTcommentsSTRINGEmployment Type: Kinds of employment contract.Employment Typeemployment_typeREFERENCEPKtype_codeSTRINGtype_nameSTRING4 valuesLeave Type: Kinds of time off.Leave Typeleave_typeREFERENCEPKtype_codeSTRINGtype_nameSTRINGis_paidBOOLEAN4 valuesCountry: Countries, identified by their ISO 3166-1 alpha-2 code.CountrycountryREFERENCEPKcountry_codeSTRINGcountry_nameSTRING6 values
Open the full-size diagram (SVG)

Open this model in EntityModel.com to explore it, change it and export it. Nothing is uploaded: the model travels in the link. Or download model.json.

Entities

Entity Type Description Primary Key Display Attribute Attributes
Department Entity An organizational unit, such as Finance or Engineering. department_id department_name 4
Employee Entity A person employed by the company. Managers are employees too. employee_id full_name 12
Employment History Entity Each position an employee has held, for promotions and transfers. history_id start_date 6
Job Entity A job definition with a pay grade, such as Software Engineer II. job_id job_title 6
Leave Request Entity A request for time off, approved by the employee's manager. leave_request_id start_date 7
Performance Review Entity A periodic review of an employee by a reviewer. review_id review_period 6
Position Entity A budgeted seat in a department for a job. A position can be vacant. position_id position_code 5
Salary Entity Base salary over time. Exactly one row per employee has no end date. salary_id annual_amount 6
Work Location Entity An office or site where employees work. work_location_id location_name 4
Country Reference Countries, identified by their ISO 3166-1 alpha-2 code. country_code country_name 2
Employment Type Reference Kinds of employment contract. type_code type_name 2
Leave Type Reference Kinds of time off. type_code type_name 3

Attributes

Department

An organizational unit, such as Finance or Engineering.

Attribute Data Type Flags Reference Example Description
department_id INT PK, Required 30
department_name STRING Label, Required Engineering
cost_center STRING Required CC-3000
head_employee_id INT 1001 The department head.

Employee

A person employed by the company. Managers are employees too.

Attribute Data Type Flags Reference Example Description
employee_id INT PK, Required 1042
employee_number STRING Required E-01042 Number on the badge and payslip.
full_name STRING Label, Required, PII Maria Gonzalez
work_email STRING Required, PII maria.gonzalez@acme.example
date_of_birth DATE PII 1988-05-21
hire_date DATE Required 2021-02-01
termination_date DATE Empty while employed.
department_id INT Required 30
position_id INT Required 301
manager_id INT 1001 Direct manager; empty for the CEO.
employment_type_code REFERENCE Required → employment_type.type_code FT
work_location_id INT Required 2

Employment History

Each position an employee has held, for promotions and transfers.

Attribute Data Type Flags Reference Example Description
history_id BIGINT PK, Required 55012
employee_id INT Required 1042
position_id INT Required 301
start_date DATE Label, Required 2023-07-01
end_date DATE Empty for the current position.
change_reason STRING Promotion

Job

A job definition with a pay grade, such as Software Engineer II.

Attribute Data Type Flags Reference Example Description
job_id INT PK, Required 12
job_title STRING Label, Required Software Engineer II
job_family STRING Engineering
pay_grade STRING Required G7
min_salary DECIMAL(12,2) 95000.00 Salary band minimum.
max_salary DECIMAL(12,2) 135000.00 Salary band maximum.

Leave Request

A request for time off, approved by the employee's manager.

Attribute Data Type Flags Reference Example Description
leave_request_id BIGINT PK, Required 40077
employee_id INT Required 1042
leave_type_code REFERENCE Required → leave_type.type_code VAC
start_date DATE Label, Required 2026-12-22
end_date DATE Required 2026-12-31
status STRING Required APPROVED Requested, approved, rejected or cancelled.
approver_id INT 1001 The manager who decided.

Performance Review

A periodic review of an employee by a reviewer.

Attribute Data Type Flags Reference Example Description
review_id BIGINT PK, Required 7010
employee_id INT Required 1042
reviewer_id INT Required 1001
review_period STRING Label, Required 2026-H1
rating INT 4 1 (below) to 5 (exceptional).
comments STRING PII

Position

A budgeted seat in a department for a job. A position can be vacant.

Attribute Data Type Flags Reference Example Description
position_id INT PK, Required 301
position_code STRING Label, Required ENG-SE2-07
department_id INT Required 30
job_id INT Required 12
is_vacant BOOLEAN Required false

Salary

Base salary over time. Exactly one row per employee has no end date.

Attribute Data Type Flags Reference Example Description
salary_id BIGINT PK, Required 88021
employee_id INT Required 1042
annual_amount DECIMAL(12,2) Label, Required, PII 118000.00
currency_code STRING Required USD ISO 4217 code.
effective_from DATE Required 2026-01-01
effective_to DATE

Work Location

An office or site where employees work.

Attribute Data Type Flags Reference Example Description
work_location_id INT PK, Required 2
location_name STRING Label, Required Austin office
city STRING Required Austin
country_code REFERENCE Required → country.country_code US

Country

Countries, identified by their ISO 3166-1 alpha-2 code.

Attribute Data Type Flags Reference Example Description
country_code STRING PK, Required US ISO 3166-1 alpha-2 code.
country_name STRING Label, Required United States English short name.

Employment Type

Kinds of employment contract.

Attribute Data Type Flags Reference Example Description
type_code STRING PK, Required FT
type_name STRING Label, Required Full-time

Leave Type

Kinds of time off.

Attribute Data Type Flags Reference Example Description
type_code STRING PK, Required VAC
type_name STRING Label, Required Vacation
is_paid BOOLEAN Required true

Relationships

Relationship Source Target Cardinality Forward Label Reverse Label Key Description
reports_to Employee Employee N:1 Reports To Manages employee.manager_id The reporting line: each employee has at most one direct manager.
member_of Employee Department N:1 Member Of Has Member employee.department_id
holds Employee Position N:1 Holds Held By employee.position_id An employee's current position.
works_at Employee Work Location N:1 Works At Site Of employee.work_location_id
headed_by Department Employee N:1 Headed By Heads department.head_employee_id
position_in Position Department N:1 In Has Position position.department_id
position_of_job Position Job N:1 Of Job Has Position position.job_id
history_of Employment History Employee N:1 History Of Has History employment_history.employee_id
history_position Employment History Position N:1 In Position Held In employment_history.position_id
paid_to Salary Employee N:1 Paid To Earns salary.employee_id
requested_by Leave Request Employee N:1 Requested By Requests leave_request.employee_id
approved_by Leave Request Employee N:1 Approved By Approves leave_request.approver_id
review_of Performance Review Employee N:1 Review Of Reviewed In performance_review.employee_id
reviewed_by Performance Review Employee N:1 Reviewed By Reviews performance_review.reviewer_id

Reference data

Country

6 values · Source: ISO 3166-1 (excerpt)

country_code country_name
US United States
CA Canada
GB United Kingdom
DE Germany
FR France
AU Australia

Employment Type

4 values · Source: Example values

type_code type_name
FT Full-time
PT Part-time
CON Contractor
INT Intern

Leave Type

4 values · Source: Example values

type_code type_name is_paid
VAC Vacation true
SICK Sick leave true
PARENT Parental leave true
UNPAID Unpaid leave false

SQL DDL (PostgreSQL)

Generated from the model. In the app, Export → Code also writes MySQL, Snowflake and Databricks DDL, DBML, dbt schema.yml and data contracts.

-- Human Resources (HR) — data model
-- PostgreSQL DDL, generated 2026-11-03
-- Made with EntityModel.com

-- Reference list: Employment Type
CREATE TABLE employment_type (
  type_code TEXT NOT NULL,
  type_name TEXT NOT NULL,
  PRIMARY KEY (type_code)
);
COMMENT ON TABLE employment_type IS 'Kinds of employment contract.';

-- Reference list: Leave Type
CREATE TABLE leave_type (
  type_code TEXT NOT NULL,
  type_name TEXT NOT NULL,
  is_paid BOOLEAN NOT NULL,
  PRIMARY KEY (type_code)
);
COMMENT ON TABLE leave_type IS 'Kinds of time off.';

-- Reference list: Country
CREATE TABLE country (
  country_code TEXT NOT NULL,
  country_name TEXT NOT NULL,
  PRIMARY KEY (country_code)
);
COMMENT ON TABLE country IS 'Countries, identified by their ISO 3166-1 alpha-2 code.';
COMMENT ON COLUMN country.country_code IS 'ISO 3166-1 alpha-2 code.';
COMMENT ON COLUMN country.country_name IS 'English short name.';

CREATE TABLE employee (
  employee_id INTEGER NOT NULL,
  employee_number TEXT NOT NULL,
  full_name TEXT NOT NULL,
  work_email TEXT NOT NULL,
  date_of_birth DATE,
  hire_date DATE NOT NULL,
  termination_date DATE,
  department_id INTEGER NOT NULL,
  position_id INTEGER NOT NULL,
  manager_id INTEGER,
  employment_type_code TEXT NOT NULL,
  work_location_id INTEGER NOT NULL,
  PRIMARY KEY (employee_id)
);
COMMENT ON TABLE employee IS 'A person employed by the company. Managers are employees too.';
COMMENT ON COLUMN employee.employee_number IS 'Number on the badge and payslip.';
COMMENT ON COLUMN employee.termination_date IS 'Empty while employed.';
COMMENT ON COLUMN employee.manager_id IS 'Direct manager; empty for the CEO.';

CREATE TABLE department (
  department_id INTEGER NOT NULL,
  department_name TEXT NOT NULL,
  cost_center TEXT NOT NULL,
  head_employee_id INTEGER,
  PRIMARY KEY (department_id)
);
COMMENT ON TABLE department IS 'An organizational unit, such as Finance or Engineering.';
COMMENT ON COLUMN department.head_employee_id IS 'The department head.';

CREATE TABLE job (
  job_id INTEGER NOT NULL,
  job_title TEXT NOT NULL,
  job_family TEXT,
  pay_grade TEXT NOT NULL,
  min_salary NUMERIC(12,2),
  max_salary NUMERIC(12,2),
  PRIMARY KEY (job_id)
);
COMMENT ON TABLE job IS 'A job definition with a pay grade, such as Software Engineer II.';
COMMENT ON COLUMN job.min_salary IS 'Salary band minimum.';
COMMENT ON COLUMN job.max_salary IS 'Salary band maximum.';

CREATE TABLE position (
  position_id INTEGER NOT NULL,
  position_code TEXT NOT NULL,
  department_id INTEGER NOT NULL,
  job_id INTEGER NOT NULL,
  is_vacant BOOLEAN NOT NULL,
  PRIMARY KEY (position_id)
);
COMMENT ON TABLE position IS 'A budgeted seat in a department for a job. A position can be vacant.';

CREATE TABLE work_location (
  work_location_id INTEGER NOT NULL,
  location_name TEXT NOT NULL,
  city TEXT NOT NULL,
  country_code TEXT NOT NULL,
  PRIMARY KEY (work_location_id)
);
COMMENT ON TABLE work_location IS 'An office or site where employees work.';

CREATE TABLE employment_history (
  history_id BIGINT NOT NULL,
  employee_id INTEGER NOT NULL,
  position_id INTEGER NOT NULL,
  start_date DATE NOT NULL,
  end_date DATE,
  change_reason TEXT,
  PRIMARY KEY (history_id)
);
COMMENT ON TABLE employment_history IS 'Each position an employee has held, for promotions and transfers.';
COMMENT ON COLUMN employment_history.end_date IS 'Empty for the current position.';

CREATE TABLE salary (
  salary_id BIGINT NOT NULL,
  employee_id INTEGER NOT NULL,
  annual_amount NUMERIC(12,2) NOT NULL,
  currency_code TEXT NOT NULL,
  effective_from DATE NOT NULL,
  effective_to DATE,
  PRIMARY KEY (salary_id)
);
COMMENT ON TABLE salary IS 'Base salary over time. Exactly one row per employee has no end date.';
COMMENT ON COLUMN salary.currency_code IS 'ISO 4217 code.';

CREATE TABLE leave_request (
  leave_request_id BIGINT NOT NULL,
  employee_id INTEGER NOT NULL,
  leave_type_code TEXT NOT NULL,
  start_date DATE NOT NULL,
  end_date DATE NOT NULL,
  status TEXT NOT NULL,
  approver_id INTEGER,
  PRIMARY KEY (leave_request_id)
);
COMMENT ON TABLE leave_request IS 'A request for time off, approved by the employee''s manager.';
COMMENT ON COLUMN leave_request.status IS 'Requested, approved, rejected or cancelled.';
COMMENT ON COLUMN leave_request.approver_id IS 'The manager who decided.';

CREATE TABLE performance_review (
  review_id BIGINT NOT NULL,
  employee_id INTEGER NOT NULL,
  reviewer_id INTEGER NOT NULL,
  review_period TEXT NOT NULL,
  rating INTEGER,
  comments TEXT,
  PRIMARY KEY (review_id)
);
COMMENT ON TABLE performance_review IS 'A periodic review of an employee by a reviewer.';
COMMENT ON COLUMN performance_review.rating IS '1 (below) to 5 (exceptional).';

-- Keys between tables
ALTER TABLE employee ADD CONSTRAINT uq_employee_employee_number UNIQUE (employee_number);
ALTER TABLE employee ADD CONSTRAINT uq_employee_work_email UNIQUE (work_email);
ALTER TABLE position ADD CONSTRAINT uq_position_position_code UNIQUE (position_code);
ALTER TABLE employee ADD CONSTRAINT fk_employee_employment_type_code FOREIGN KEY (employment_type_code) REFERENCES employment_type (type_code);
ALTER TABLE work_location ADD CONSTRAINT fk_work_location_country_code FOREIGN KEY (country_code) REFERENCES country (country_code);
ALTER TABLE leave_request ADD CONSTRAINT fk_leave_request_leave_type_code FOREIGN KEY (leave_type_code) REFERENCES leave_type (type_code);
ALTER TABLE employee ADD CONSTRAINT fk_reports_to FOREIGN KEY (manager_id) REFERENCES employee (employee_id);
ALTER TABLE employee ADD CONSTRAINT fk_member_of FOREIGN KEY (department_id) REFERENCES department (department_id);
ALTER TABLE employee ADD CONSTRAINT fk_holds FOREIGN KEY (position_id) REFERENCES position (position_id);
ALTER TABLE employee ADD CONSTRAINT fk_works_at FOREIGN KEY (work_location_id) REFERENCES work_location (work_location_id);
ALTER TABLE department ADD CONSTRAINT fk_headed_by FOREIGN KEY (head_employee_id) REFERENCES employee (employee_id);
ALTER TABLE position ADD CONSTRAINT fk_position_in FOREIGN KEY (department_id) REFERENCES department (department_id);
ALTER TABLE position ADD CONSTRAINT fk_position_of_job FOREIGN KEY (job_id) REFERENCES job (job_id);
ALTER TABLE employment_history ADD CONSTRAINT fk_history_of FOREIGN KEY (employee_id) REFERENCES employee (employee_id);
ALTER TABLE employment_history ADD CONSTRAINT fk_history_position FOREIGN KEY (position_id) REFERENCES position (position_id);
ALTER TABLE salary ADD CONSTRAINT fk_paid_to FOREIGN KEY (employee_id) REFERENCES employee (employee_id);
ALTER TABLE leave_request ADD CONSTRAINT fk_requested_by FOREIGN KEY (employee_id) REFERENCES employee (employee_id);
ALTER TABLE leave_request ADD CONSTRAINT fk_approved_by FOREIGN KEY (approver_id) REFERENCES employee (employee_id);
ALTER TABLE performance_review ADD CONSTRAINT fk_review_of FOREIGN KEY (employee_id) REFERENCES employee (employee_id);
ALTER TABLE performance_review ADD CONSTRAINT fk_reviewed_by FOREIGN KEY (reviewer_id) REFERENCES employee (employee_id);

-- Reference values
INSERT INTO employment_type (type_code, type_name) VALUES
  ('FT', 'Full-time'),
  ('PT', 'Part-time'),
  ('CON', 'Contractor'),
  ('INT', 'Intern');

INSERT INTO leave_type (type_code, type_name, is_paid) VALUES
  ('VAC', 'Vacation', TRUE),
  ('SICK', 'Sick leave', TRUE),
  ('PARENT', 'Parental leave', TRUE),
  ('UNPAID', 'Unpaid leave', FALSE);

INSERT INTO country (country_code, country_name) VALUES
  ('US', 'United States'),
  ('CA', 'Canada'),
  ('GB', 'United Kingdom'),
  ('DE', 'Germany'),
  ('FR', 'France'),
  ('AU', 'Australia');

-- Hierarchies (navigation over the relationships above):
--   Reporting Line: root employee, 2 level(s)

Mermaid

Paste into a mermaid code block on GitHub, GitLab or Notion to show the diagram there.

---
title: Human Resources (HR)
---
%% Made with EntityModel.com
erDiagram
  employee {
    int employee_id PK "Employee ID"
    string employee_number "Number on the badge and payslip."
    string full_name "Full Name"
    string work_email "Work Email"
    date date_of_birth "Date Of Birth"
    date hire_date "Hire Date"
    date termination_date "Empty while employed."
    int department_id FK "Department ID"
    int position_id FK "Position ID"
    int manager_id FK "Direct manager; empty for the CEO."
    string employment_type_code FK "Employment Type Code"
    int work_location_id FK "Work Location ID"
  }
  department {
    int department_id PK "Department ID"
    string department_name "Department Name"
    string cost_center "Cost Center"
    int head_employee_id FK "The department head."
  }
  job {
    int job_id PK "Job ID"
    string job_title "Job Title"
    string job_family "Job Family"
    string pay_grade "Pay Grade"
    decimal min_salary "Salary band minimum."
    decimal max_salary "Salary band maximum."
  }
  position {
    int position_id PK "Position ID"
    string position_code "Position Code"
    int department_id FK "Department ID"
    int job_id FK "Job ID"
    boolean is_vacant "Is Vacant"
  }
  work_location {
    int work_location_id PK "Work Location ID"
    string location_name "Location Name"
    string city "City"
    string country_code FK "Country Code"
  }
  employment_history {
    bigint history_id PK "History ID"
    int employee_id FK "Employee ID"
    int position_id FK "Position ID"
    date start_date "Start Date"
    date end_date "Empty for the current position."
    string change_reason "Change Reason"
  }
  salary {
    bigint salary_id PK "Salary ID"
    int employee_id FK "Employee ID"
    decimal annual_amount "Annual Amount"
    string currency_code "ISO 4217 code."
    date effective_from "Effective From"
    date effective_to "Effective To"
  }
  leave_request {
    bigint leave_request_id PK "Leave Request ID"
    int employee_id FK "Employee ID"
    string leave_type_code FK "Leave Type Code"
    date start_date "Start Date"
    date end_date "End Date"
    string status "Requested, approved, rejected or cancelled."
    int approver_id FK "The manager who decided."
  }
  performance_review {
    bigint review_id PK "Review ID"
    int employee_id FK "Employee ID"
    int reviewer_id FK "Reviewer ID"
    string review_period "Review Period"
    int rating "1 (below) to 5 (exceptional)."
    string comments "Comments"
  }
  employment_type {
    string type_code PK "Type Code"
    string type_name "Type Name"
  }
  leave_type {
    string type_code PK "Type Code"
    string type_name "Type Name"
    boolean is_paid "Is Paid"
  }
  country {
    string country_code PK "ISO 3166-1 alpha-2 code."
    string country_name "English short name."
  }
  employee }o--|| employee : "Reports To"
  employee }o--|| department : "Member Of"
  employee }o--|| position : "Holds"
  employee }o--|| work_location : "Works At"
  department }o--|| employee : "Headed By"
  position }o--|| department : "In"
  position }o--|| job : "Of Job"
  employment_history }o--|| employee : "History Of"
  employment_history }o--|| position : "In Position"
  salary }o--|| employee : "Paid To"
  leave_request }o--|| employee : "Requested By"
  leave_request }o--|| employee : "Approved By"
  performance_review }o--|| employee : "Review Of"
  performance_review }o--|| employee : "Reviewed By"
  employee }o..|| employment_type : "Employment Type Code"
  work_location }o..|| country : "Country Code"
  leave_request }o..|| leave_type : "Leave Type Code"

Frequently asked questions

How do you model a manager–employee relationship in a database?

Add a manager_id column on the employee table that references the same table's employee_id. Each employee has at most one direct manager, and walking the relationship upward gives the reporting chain.

What is the difference between a job and a position in HR data?

A job describes the work and its pay band, and many people can share it. A position is one budgeted seat for a job in a department; it is filled by one employee or is vacant.