Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions calculate_largest_expensors.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- This query identifies employees who have exceeded the spending threshold
-- It calculates total expenses for each employee and compares against the limit
-- The results include the employee's manager for approval chain visibility
-- This analysis helps identify potential policy violations

USE memory.default;

WITH employee_expenses AS (
-- Calculate total expenses for each employee
-- Multiply unit price by quantity to get the total amount for each expense
-- Group by employee to get their total spending
SELECT
employee_id,
SUM(unit_price * quantity) AS total_spent
FROM EXPENSE
GROUP BY employee_id
)
-- Join with employee data to get names and manager information
-- Filter for employees who exceeded the spending threshold
-- Order results by total amount spent in descending order
SELECT
ee.employee_id,
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
e.manager_id,
CONCAT(m.first_name, ' ', m.last_name) AS manager_name,
ee.total_spent AS total_expensed_amount
FROM employee_expenses ee
JOIN EMPLOYEE e ON ee.employee_id = e.employee_id
LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id
WHERE ee.total_spent > 1000
ORDER BY ee.total_spent DESC;
22 changes: 22 additions & 0 deletions create_employees.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- This file creates the EMPLOYEE table and loads our employee data
USE memory.default;

CREATE TABLE EMPLOYEE (
employee_id TINYINT,
first_name VARCHAR,
last_name VARCHAR,
job_title VARCHAR,
manager_id TINYINT
);

-- Loading data from hr/employee_index.csv
INSERT INTO EMPLOYEE VALUES
(1, 'Ian', 'James', 'CEO', 4),
(2, 'Umberto', 'Torrielli', 'CSO', 1),
(3, 'Alex', 'Jacobson', 'MD EMEA', 2),
(4, 'Darren', 'Poynton', 'CFO', 2),
(5, 'Tim', 'Beard', 'MD APAC', 2),
(6, 'Gemma', 'Dodd', 'COS', 1),
(7, 'Lisa', 'Platten', 'CHR', 6),
(8, 'Stefano', 'Camisaca', 'GM Activation', 2),
(9, 'Andrea', 'Ghibaudi', 'MD NAM', 2);
18 changes: 18 additions & 0 deletions create_expenses.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- This file creates the EXPENSE table and loads all our receipts
USE memory.default;

CREATE TABLE EXPENSE (
employee_id TINYINT,
unit_price DECIMAL(8, 2),
quantity TINYINT
);

-- Loading data from finance/receipts_from_last_night/*.txt
INSERT INTO EXPENSE VALUES
(3, 13.0, 75),
(3, 22.0, 18),
(4, 40.0, 9),
(9, 300.0, 1),
(2, 17.5, 4),
(3, 11.0, 20),
(3, 6.5, 14);
32 changes: 32 additions & 0 deletions create_invoices.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- This file creates the SUPPLIER and INVOICE tables and loads our invoice data
USE memory.default;

-- First create the SUPPLIER table
CREATE TABLE SUPPLIER (
supplier_id TINYINT,
name VARCHAR
);

-- Load supplier data from finance/invoices_due/*.txt
INSERT INTO SUPPLIER VALUES
(1, 'Catering Plus'),
(2, 'Dave''s Discos'),
(3, 'Entertainment tonight'),
(4, 'Ice Ice Baby'),
(5, 'Party Animals');

-- Now create the INVOICE table
CREATE TABLE INVOICE (
supplier_id TINYINT,
invoice_amount DECIMAL(8, 2),
due_date DATE
);

-- Load invoice data from finance/invoices_due/*.txt
INSERT INTO INVOICE VALUES
(1, 1500.0, DATE '2025-07-31'),
(2, 500.0, DATE '2025-07-31'),
(4, 4000.0, DATE '2025-07-31'),
(1, 2000.0, DATE '2025-07-31'),
(3, 6000.0, DATE '2025-07-31'),
(5, 6000.0, DATE '2025-07-31');
22 changes: 22 additions & 0 deletions find_employee_expenses.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- This query retrieves all expenses for a specific employee
-- It joins the EXPENSE table with the EMPLOYEE table to get complete information
-- This provides visibility into employee spending patterns
-- It also shows who approved each expense for accountability purposes

USE memory.default;

SELECT
e.expense_id,
e.employee_id,
emp.first_name,
emp.last_name,
e.amount,
e.description,
e.date,
e.approved_by,
approver.first_name as approver_first_name,
approver.last_name as approver_last_name
FROM EXPENSE e
JOIN EMPLOYEE emp ON e.employee_id = emp.employee_id
LEFT JOIN EMPLOYEE approver ON e.approved_by = approver.employee_id
WHERE e.employee_id = 1; -- We can replace with the employee ID based on which we want to check!
40 changes: 40 additions & 0 deletions find_employee_managers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- This query retrieves the complete management chain for a specific employee
-- It uses a recursive CTE to traverse the entire management hierarchy
-- This provides visibility into the organizational structure
-- It shows all managers in the approval chain for an employee

USE memory.default;

WITH RECURSIVE management_chain AS (
-- Base case: the employee's direct manager
-- Start with the immediate supervisor
SELECT
e.employee_id,
e.first_name,
e.last_name,
e.manager_id,
1 as level
FROM EMPLOYEE e
WHERE e.employee_id = 1 -- We can replace with the employee ID based on which we want to check!

UNION ALL

-- Recursive case: continue up the management chain
-- For each manager, find their manager, and so on
-- This builds the complete approval hierarchy
SELECT
m.employee_id,
m.first_name,
m.last_name,
m.manager_id,
mc.level + 1
FROM EMPLOYEE m
JOIN management_chain mc ON m.employee_id = mc.manager_id
)
SELECT
employee_id,
first_name,
last_name,
level
FROM management_chain
ORDER BY level;
42 changes: 42 additions & 0 deletions find_manager_cycles.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-- This query identifies cycles in the management hierarchy
-- A cycle occurs when an employee reports to someone who eventually reports back to them
-- These cycles can cause issues with expense approval workflows
-- This analysis helps identify organizational structure problems

USE memory.default;

WITH RECURSIVE manager_chain(employee_id, manager_id, path, depth) AS (
-- Start with each employee and their direct manager
-- Initialize the recursive traversal of the management hierarchy
SELECT
employee_id,
manager_id,
CAST(employee_id AS VARCHAR) as path,
1 as depth
FROM EMPLOYEE

UNION ALL

-- Continue traversing the management chain upward
-- For each employee, examine their manager's manager, and so on
-- This builds a complete path of the management chain
SELECT
e.employee_id,
e.manager_id,
mc.path || ',' || CAST(e.employee_id AS VARCHAR),
mc.depth + 1
FROM EMPLOYEE e
JOIN manager_chain mc ON e.employee_id = mc.manager_id
WHERE mc.depth < 10 -- Limit recursion depth to prevent infinite loops
-- Maximum depth of 10 levels is sufficient for most organizational structures
)
-- Identify paths that contain the same employee more than once
-- This indicates a cycle in the management hierarchy
-- These cycles need to be addressed to ensure proper expense approval workflows
SELECT DISTINCT
employee_id,
path as cycle
FROM manager_chain
WHERE path LIKE '%,' || CAST(employee_id AS VARCHAR) || ',%' -- Employee appears in middle of path
OR path LIKE CAST(employee_id AS VARCHAR) || ',%' -- Employee appears at start of path
OR path LIKE '%,' || CAST(employee_id AS VARCHAR); -- Employee appears at end of path
88 changes: 88 additions & 0 deletions generate_supplier_payment_plans.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
-- This query generates a payment plan for each supplier
-- It calculates monthly payment amounts to clear invoices before due dates
-- The plan ensures all invoices are paid in full by their respective due dates
-- This analysis helps with cash flow management and vendor relationships

USE memory.default;

WITH supplier_totals AS (
-- Calculate total amount owed to each supplier
-- This is the starting point for our payment plan
SELECT
supplier_id,
SUM(invoice_amount) AS total_amount,
MAX(due_date) AS latest_due_date
FROM INVOICE
GROUP BY supplier_id
),
payment_months AS (
-- Determine how many months we have to pay each supplier
-- Calculate from current date to the latest due date
SELECT
supplier_id,
total_amount,
latest_due_date,
-- Calculate months between now and due date, rounded up
CEIL(DATEDIFF('month', CURRENT_DATE, latest_due_date)) AS months_to_pay
FROM supplier_totals
),
monthly_payments AS (
-- Generate a row for each payment month
-- This creates the payment schedule for each supplier
SELECT
supplier_id,
total_amount,
latest_due_date,
months_to_pay,
-- Calculate payment amount for each month
-- For the last month, pay the remaining balance
CASE
WHEN payment_month = months_to_pay THEN
total_amount - (FLOOR(total_amount / months_to_pay) * (months_to_pay - 1))
ELSE
FLOOR(total_amount / months_to_pay)
END AS payment_amount,
-- Calculate the payment date (last day of each month)
-- Start from current month and increment for each payment
DATE_ADD('month', payment_month - 1,
DATE_ADD('day',
-1,
DATE_ADD('month', 1,
DATE_TRUNC('month', CURRENT_DATE)
)
)
) AS payment_date,
-- Calculate remaining balance after each payment
total_amount - (FLOOR(total_amount / months_to_pay) * payment_month) AS balance_outstanding
FROM payment_months
CROSS JOIN (
-- Generate a sequence of payment months
SELECT payment_month
FROM (
SELECT 1 AS payment_month
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9
UNION ALL SELECT 10
UNION ALL SELECT 11
UNION ALL SELECT 12
) months
WHERE payment_month <= (SELECT MAX(months_to_pay) FROM payment_months)
) payment_sequence
)
-- Join with supplier information and format the results
-- This provides a complete payment schedule for each supplier
SELECT
mp.supplier_id,
s.name AS supplier_name,
mp.payment_amount,
mp.balance_outstanding,
mp.payment_date
FROM monthly_payments mp
JOIN SUPPLIER s ON mp.supplier_id = s.supplier_id
ORDER BY mp.supplier_id, mp.payment_date;
Loading