diff --git a/calculate_largest_expensors.sql b/calculate_largest_expensors.sql index e69de29..52399dd 100644 --- a/calculate_largest_expensors.sql +++ b/calculate_largest_expensors.sql @@ -0,0 +1,64 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- Main query to identify employees who expensed more than 1000 +SELECT + e.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, + SUM(exp.unit_price * exp.quantity) AS total_expensed_amount +FROM EMPLOYEE e +LEFT JOIN EXPENSE exp ON e.employee_id = exp.employee_id +LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id +GROUP BY + e.employee_id, + e.first_name, + e.last_name, + e.manager_id, + m.first_name, + m.last_name +HAVING SUM(exp.unit_price * exp.quantity) > 1000 +ORDER BY total_expensed_amount DESC; + +-- Verify expense calculations for all employees +SELECT + e.employee_id, + CONCAT(e.first_name, ' ', e.last_name) AS employee_name, + COUNT(exp.employee_id) AS number_of_receipts, + COALESCE(SUM(exp.unit_price * exp.quantity), 0) AS total_expensed_amount, + CASE + WHEN COALESCE(SUM(exp.unit_price * exp.quantity), 0) > 1000 THEN 'EXCEEDS LIMIT' + ELSE 'WITHIN LIMIT' + END AS status +FROM EMPLOYEE e +LEFT JOIN EXPENSE exp ON e.employee_id = exp.employee_id +GROUP BY + e.employee_id, + e.first_name, + e.last_name +ORDER BY total_expensed_amount DESC; + + + +-- Alternative implementation using Common Table Expression for clarity +-- This approach separates expense calculation from employee details +WITH EmployeeExpenses AS ( + -- Calculate total expenses for each employee + SELECT + employee_id, + SUM(unit_price * quantity) AS total_expensed_amount + FROM EXPENSE + GROUP BY employee_id + HAVING SUM(unit_price * quantity) > 1000 +) +SELECT + e.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_expensed_amount +FROM EmployeeExpenses ee +JOIN EMPLOYEE e ON ee.employee_id = e.employee_id +LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id +ORDER BY ee.total_expensed_amount DESC; diff --git a/create_employees.sql b/create_employees.sql index e69de29..043b23c 100644 --- a/create_employees.sql +++ b/create_employees.sql @@ -0,0 +1,52 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- Drop table if it already exists +DROP TABLE IF EXISTS EMPLOYEE; + +-- Create the EMPLOYEE table with appropriate data types +CREATE TABLE EMPLOYEE ( + employee_id TINYINT, + first_name VARCHAR, + last_name VARCHAR, + job_title VARCHAR, + manager_id TINYINT +); + +-- Manually insert the employee data as Trino doesn't support direct CSV import +INSERT INTO EMPLOYEE (employee_id, first_name, last_name, job_title, manager_id) 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); + +-- Verify the table was created successfully +SHOW TABLES FROM memory.default; + +-- Check table structure +DESCRIBE memory.default.EMPLOYEE; + +-- Verify all data was inserted correctly +SELECT * FROM EMPLOYEE ORDER BY employee_id; + +-- Check for any invalid manager references (managers that don't exist as employees) +SELECT e.employee_id, e.first_name, e.last_name, e.manager_id +FROM EMPLOYEE e +LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id +WHERE e.manager_id IS NOT NULL AND m.employee_id IS NULL; + +-- Display employee hierarchy for verification +SELECT + e.employee_id, + CONCAT(e.first_name, ' ', e.last_name) AS employee_name, + e.job_title, + e.manager_id, + CONCAT(m.first_name, ' ', m.last_name) AS manager_name +FROM EMPLOYEE e +LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id +ORDER BY e.employee_id; diff --git a/create_expenses.sql b/create_expenses.sql index e69de29..f9b83ba 100644 --- a/create_expenses.sql +++ b/create_expenses.sql @@ -0,0 +1,60 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- Drop table if it already exists +DROP TABLE IF EXISTS EXPENSE; + +-- Create the EXPENSE table with appropriate data types +CREATE TABLE EXPENSE ( + employee_id TINYINT, + unit_price DECIMAL(8, 2), + quantity TINYINT +); + +-- Manually insert expense data from finance/receipts_from_last_night/*.txt files +INSERT INTO EXPENSE (employee_id, unit_price, quantity) VALUES + (3, 6.50, 14), + (3, 11.00, 20), + (3, 22.00, 18), + (3, 13.00, 75), + (9, 300.00, 1), + (4, 40.00, 9), + (2, 17.50, 4); + +-- Verify the table was created successfully +SHOW TABLES FROM memory.default; + +-- Check table structure +DESCRIBE memory.default.EXPENSE; + +-- Verify all data was inserted correctly +SELECT * FROM EXPENSE ORDER BY employee_id, unit_price; + +-- Check for any invalid employee references (expenses for employees that don't exist) +SELECT e.employee_id, e.unit_price, e.quantity +FROM EXPENSE e +LEFT JOIN EMPLOYEE emp ON e.employee_id = emp.employee_id +WHERE emp.employee_id IS NULL; + +-- Display expenses with employee details for verification +SELECT + e.employee_id, + CONCAT(emp.first_name, ' ', emp.last_name) AS employee_name, + emp.job_title, + e.unit_price, + e.quantity, + (e.unit_price * e.quantity) AS total_amount +FROM EXPENSE e +LEFT JOIN EMPLOYEE emp ON e.employee_id = emp.employee_id +ORDER BY e.employee_id, e.unit_price; + +-- Calculate total expenses per employee +SELECT + e.employee_id, + CONCAT(emp.first_name, ' ', emp.last_name) AS employee_name, + COUNT(*) AS number_of_receipts, + SUM(e.unit_price * e.quantity) AS total_expensed_amount +FROM EXPENSE e +LEFT JOIN EMPLOYEE emp ON e.employee_id = emp.employee_id +GROUP BY e.employee_id, emp.first_name, emp.last_name +ORDER BY total_expensed_amount DESC; diff --git a/create_invoices.sql b/create_invoices.sql index e69de29..caaa563 100644 --- a/create_invoices.sql +++ b/create_invoices.sql @@ -0,0 +1,87 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- Drop tables if they already exist +DROP TABLE IF EXISTS INVOICE; +DROP TABLE IF EXISTS SUPPLIER; + +-- Create the SUPPLIER table with appropriate data types +CREATE TABLE SUPPLIER ( + supplier_id TINYINT, + name VARCHAR +); + +-- Insert suppliers sorted alphabetically by name +INSERT INTO SUPPLIER (supplier_id, name) VALUES + (1, 'Catering Plus'), + (2, 'Dave''s Discos'), + (3, 'Entertainment tonight'), + (4, 'Ice Ice Baby'), + (5, 'Party Animals'); + +-- Create the INVOICE table with appropriate data types as specified in requirements +CREATE TABLE INVOICE ( + supplier_id TINYINT, + invoice_amount DECIMAL(8, 2), + due_date DATE +); + +-- Manually insert invoice data from finance/invoices_due/*.txt files +INSERT INTO INVOICE (supplier_id, invoice_amount, due_date) VALUES + (1, 2000.00, DATE '2025-02-28'), + (1, 1500.00, DATE '2025-03-31'), + (2, 500.00, DATE '2025-01-31'), + (3, 6000.00, DATE '2025-03-31'), + (4, 4000.00, DATE '2025-06-30'), + (5, 6000.00, DATE '2025-03-31'); + +-- Verify the tables were created successfully +SHOW TABLES FROM memory.default; + +-- Check table structures +DESCRIBE memory.default.SUPPLIER; +DESCRIBE memory.default.INVOICE; + +-- Verify all supplier data was inserted correctly +SELECT * FROM SUPPLIER ORDER BY supplier_id; + +-- Verify all invoice data was inserted correctly +SELECT * FROM INVOICE ORDER BY supplier_id, due_date; + +-- Check for any invalid supplier references (invoices for suppliers that don't exist) +SELECT i.supplier_id, i.invoice_amount, i.due_date +FROM INVOICE i +LEFT JOIN SUPPLIER s ON i.supplier_id = s.supplier_id +WHERE s.supplier_id IS NULL; + +-- Display invoices with supplier details for verification +SELECT + i.supplier_id, + s.name AS supplier_name, + i.invoice_amount, + i.due_date +FROM INVOICE i +LEFT JOIN SUPPLIER s ON i.supplier_id = s.supplier_id +ORDER BY i.supplier_id, i.due_date; + +-- Calculate total invoices per supplier +SELECT + i.supplier_id, + s.name AS supplier_name, + COUNT(*) AS number_of_invoices, + SUM(i.invoice_amount) AS total_invoice_amount +FROM INVOICE i +LEFT JOIN SUPPLIER s ON i.supplier_id = s.supplier_id +GROUP BY i.supplier_id, s.name +ORDER BY i.supplier_id; + +-- Verify all due dates are last day of the month +SELECT + supplier_id, + invoice_amount, + due_date, + CASE + WHEN due_date = LAST_DAY_OF_MONTH(due_date) THEN 'Valid' + ELSE 'Invalid - not last day of month' + END AS date_validation +FROM INVOICE; diff --git a/find_manager_cycles.sql b/find_manager_cycles.sql index e69de29..d231cc5 100644 --- a/find_manager_cycles.sql +++ b/find_manager_cycles.sql @@ -0,0 +1,48 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- This query identifies cycles in the employee-manager hierarchy +WITH RECURSIVE manager_chain (employee_id, current_manager, path, depth, start_employee) AS ( + -- Start with each employee and their manager + SELECT + employee_id, + manager_id AS current_manager, + CAST(employee_id AS VARCHAR) AS path, + 1 AS depth, + employee_id AS start_employee + FROM EMPLOYEE + WHERE manager_id IS NOT NULL + + UNION ALL + + -- Follow the management chain + SELECT + mc.employee_id, + e.manager_id AS current_manager, + CONCAT(mc.path, ',', CAST(e.employee_id AS VARCHAR)) AS path, + mc.depth + 1, + mc.start_employee + FROM manager_chain mc + JOIN EMPLOYEE e ON mc.current_manager = e.employee_id + WHERE mc.depth < 10 -- Prevent infinite loops + AND e.manager_id IS NOT NULL -- Only continue if there's a next manager + AND e.employee_id != mc.start_employee -- Stop before we would revisit the starting employee +) + +SELECT DISTINCT + mc.start_employee AS employee_id, + CONCAT(mc.path, ',', CAST(mc.current_manager AS VARCHAR)) AS cycle +FROM manager_chain mc +WHERE mc.current_manager = mc.start_employee -- The current manager is the same as where we started +ORDER BY employee_id; + +-- Show employee hierarchy for verification +SELECT + e.employee_id, + CONCAT(e.first_name, ' ', e.last_name) AS employee_name, + e.job_title, + e.manager_id, + CONCAT(m.first_name, ' ', m.last_name) AS manager_name +FROM EMPLOYEE e +LEFT JOIN EMPLOYEE m ON e.manager_id = m.employee_id +ORDER BY e.employee_id; diff --git a/generate_supplier_payment_plans.sql b/generate_supplier_payment_plans.sql index e69de29..d0595d2 100644 --- a/generate_supplier_payment_plans.sql +++ b/generate_supplier_payment_plans.sql @@ -0,0 +1,68 @@ +-- When interacting with trino, `USE` the `memory.default` catalogue.schema. +USE memory.default; + +-- Main query to generate supplier payment plans with $1500 flat monthly payments +WITH supplier_totals AS ( + -- Calculate total amount due for each supplier across all invoices + SELECT + s.supplier_id, + s.name AS supplier_name, + SUM(i.invoice_amount) AS total_due + FROM SUPPLIER s + JOIN INVOICE i ON s.supplier_id = i.supplier_id + GROUP BY s.supplier_id, s.name +), +payment_installments AS ( + -- Generate payment installments for each supplier + SELECT + st.supplier_id, + st.supplier_name, + st.total_due, + -- Calculate number of payments needed (total_due / 1500, rounded up) + CEIL(st.total_due / 1500.00) AS payment_count, + -- Generate payment numbers (1, 2, 3, etc.) + payment_number + FROM supplier_totals st + CROSS JOIN UNNEST(sequence(1, CAST(CEIL(st.total_due / 1500.00) AS BIGINT))) AS t(payment_number) +), +calculated_payments AS ( + -- Calculate payment amounts and dates + SELECT + pi.supplier_id, + pi.supplier_name, + pi.total_due, + -- Payment amount is $1500, except for final payment which is remaining balance + CASE + WHEN pi.payment_number = pi.payment_count THEN + pi.total_due - (1500.00 * (pi.payment_number - 1)) + ELSE 1500.00 + END AS payment_amount, + -- Payment date is last day of month, starting from current month + DATE_ADD('month', pi.payment_number - 1, LAST_DAY_OF_MONTH(CURRENT_DATE)) AS payment_date, + pi.payment_number + FROM payment_installments pi +) +-- Final result with running balance calculation +SELECT + cp.supplier_id, + cp.supplier_name, + ROUND(cp.payment_amount, 2) AS payment_amount, + ROUND(cp.total_due - SUM(cp.payment_amount) OVER ( + PARTITION BY cp.supplier_id + ORDER BY cp.payment_date + ), 2) AS balance_outstanding, + cp.payment_date +FROM calculated_payments cp +ORDER BY cp.supplier_name, cp.payment_date; + +-- Verify payment plan calculations +SELECT + s.supplier_id, + s.name AS supplier_name, + COUNT(*) AS invoice_count, + SUM(i.invoice_amount) AS total_invoice_amount, + CEIL(SUM(i.invoice_amount) / 1500.00) AS estimated_payments_needed +FROM SUPPLIER s +LEFT JOIN INVOICE i ON s.supplier_id = i.supplier_id +GROUP BY s.supplier_id, s.name +ORDER BY s.supplier_id;