diff --git a/calculate_largest_expensors.sql b/calculate_largest_expensors.sql index e69de29..e7ae98d 100644 --- a/calculate_largest_expensors.sql +++ b/calculate_largest_expensors.sql @@ -0,0 +1,27 @@ +USE memory.default; + + +-- Aggregate total expensed amount per employee + +WITH total_expensed_amount AS ( + SELECT + employee_id, + SUM(unit_price * quantity) AS total_expensed + FROM expense + GROUP BY employee_id +) + +-- List employees whose total expenses exceed the threshold +SELECT + e.employee_id, + e.first_name || ' ' || e.last_name AS employee_name, + tea.total_expensed, + e.manager_id, + m.first_name || ' ' || m.last_name AS manager_name +FROM total_expensed_amount tea +JOIN employee e + ON e.employee_id = tea.employee_id +LEFT JOIN employee m + ON e.manager_id = m.employee_id -- manager information when available +WHERE tea.total_expensed > 1000 -- threshold +ORDER BY tea.total_expensed DESC; \ No newline at end of file diff --git a/create_employees.sql b/create_employees.sql index e69de29..7367d84 100644 --- a/create_employees.sql +++ b/create_employees.sql @@ -0,0 +1,22 @@ +USE memory.default; + + +CREATE TABLE IF NOT EXISTS employee ( +employee_id TINYINT, +first_name VARCHAR, +last_name VARCHAR, +job_title VARCHAR, +manager_id TINYINT); + + +-- Since the Trino CLI does not support direct CSV ingestion, the data has been converted into explicit INSERT statements. +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); \ No newline at end of file diff --git a/create_expenses.sql b/create_expenses.sql index e69de29..d798788 100644 --- a/create_expenses.sql +++ b/create_expenses.sql @@ -0,0 +1,16 @@ +USE memory.default; + +CREATE TABLE IF NOT EXISTS expense ( +employee_id TINYINT, +unit_price DECIMAL(8, 2), +quantity TINYINT); + + + INSERT INTO expense (employee_id,unit_price, quantity) VALUES +(3, 6.5, 14), +(3, 11, 20), +(3, 22, 18), +(3, 13, 75), +(9, 300, 1), +(4, 40, 9), +(2, 17.5, 4); \ No newline at end of file diff --git a/create_invoices.sql b/create_invoices.sql index e69de29..1cab802 100644 --- a/create_invoices.sql +++ b/create_invoices.sql @@ -0,0 +1,33 @@ +USE memory.default; + +-- Raw staging table for incoming invoice data, corrected typo invoice_ammount +CREATE TABLE IF NOT EXISTS invoice_raw ( +supplier_name VARCHAR, +invoice_amount DECIMAL(8,2), +due_date DATE); + +INSERT INTO invoice_raw (supplier_name, invoice_amount, due_date) VALUES +('Party Animals', 6000, last_day_of_month(date_add('month', 3, DATE '2026-02-02'))), +('Catering Plus', 2000, last_day_of_month(date_add('month', 2, DATE '2026-02-02'))), +('Catering Plus', 1500, last_day_of_month(date_add('month', 3, DATE '2026-02-02'))), +('Daves Discos', 500, last_day_of_month(date_add('month', 1, DATE '2026-02-02'))), +('Entertainment tonight', 6000, last_day_of_month(date_add('month', 3, DATE '2026-02-02'))), +('Ice Ice Baby', 4000, last_day_of_month(date_add('month', 6, DATE '2026-02-02'))); + + +-- One row per supplier, with deterministic IDs assigned alphabetically by supplier name. +CREATE TABLE IF NOT EXISTS supplier as ( +SELECT + CAST(ROW_NUMBER() OVER (ORDER BY supplier_name)as TINYINT) as supplier_id, + supplier_name as name +FROM invoice_raw +GROUP BY supplier_name); + +-- Final invoice table with corrected typo invoice_ammount +CREATE TABLE IF NOT EXISTS invoice as( +SELECT s.supplier_id, + i.invoice_amount, + i.due_date + FROM invoice_raw i + JOIN supplier s + ON i.supplier_name = s.name); \ No newline at end of file diff --git a/find_manager_cycles.sql b/find_manager_cycles.sql index e69de29..f8d2fdc 100644 --- a/find_manager_cycles.sql +++ b/find_manager_cycles.sql @@ -0,0 +1,66 @@ +USE memory.default; + + +CREATE TABLE emp_paths AS +WITH RECURSIVE paths (root_id, employee_id, manager_id, visited, depth) AS ( + +-- Start: one path per employee + + SELECT + e.employee_id AS root_id, + e.employee_id, + e.manager_id, + ARRAY[e.employee_id] AS visited, + 1 AS depth + FROM employee e + + UNION ALL + +-- Step: move to manager, stop before revisiting a node + + SELECT + p.root_id, + m.employee_id, + m.manager_id, + p.visited || m.employee_id, + p.depth + 1 + FROM paths p + JOIN employee m + ON m.employee_id = p.manager_id + WHERE p.depth < 10 + AND NOT contains(p.visited, m.employee_id) +) +SELECT * +FROM paths; + + +-- Identify paths where the next manager would close a loop + +CREATE TABLE cycle_hits AS +SELECT + p.root_id, + p.manager_id AS loop_member, + p.visited +FROM emp_paths p +WHERE p.manager_id IS NOT NULL + AND contains(p.visited, p.manager_id); + + +-- Extract the actual cycle as a closed array + +CREATE TABLE cycles AS +SELECT + loop_member AS cycle_anchor, + slice( + visited, + array_position(visited, loop_member), + cardinality(visited) - array_position(visited, loop_member) + 1 + ) || loop_member AS loopcycle +FROM cycle_hits; + + +-- Final result: one row per detected cycle + +SELECT DISTINCT cycle_anchor, loopcycle +FROM cycles +ORDER BY cycle_anchor, loopcycle; diff --git a/generate_supplier_payment_plans.sql b/generate_supplier_payment_plans.sql index e69de29..feb8588 100644 --- a/generate_supplier_payment_plans.sql +++ b/generate_supplier_payment_plans.sql @@ -0,0 +1,97 @@ +USE memory.default; + + +WITH months as ( +-- For each invoice, compute how many end-of-month payments are needed + SELECT + supplier_id, + invoice_amount, + due_date, + date_diff('month', last_day_of_month(DATE '2026-02-02'), due_date) + 1 AS months_to_pay +FROM invoice +), + +monthly_rates as ( +-- Uniform monthly rate per invoice (invoice amount spread across months_to_pay) + SELECT + supplier_id, + invoice_amount, + due_date, + months_to_pay, + ROUND(invoice_amount/months_to_pay, 2) as rate_amount + FROM months +), + +invoice_payment_schedule as ( +-- Month offsets per invoice + SELECT + supplier_id, + invoice_amount, + due_date, + months_to_pay, + rate_amount, + sequence(0, months_to_pay - 1) AS month_offsets +FROM monthly_rates + ), + +invoice_payment_schedule_exploded as ( +-- One row per invoice per month payment + SELECT + supplier_id, + invoice_amount, + due_date, + months_to_pay, + rate_amount, + month_index +FROM invoice_payment_schedule +CROSS JOIN UNNEST (month_offsets) AS t(month_index) +), + +invoice_payment_calendar AS ( +-- Convert offsets into actual end-of-month payment dates + SELECT + supplier_id, + invoice_amount, + rate_amount, + last_day_of_month(date_add('month',month_index,last_day_of_month(DATE '2026-02-02'))) AS payment_date + FROM invoice_payment_schedule_exploded +), + +supplier_payment_amount as ( +-- Suppliers receive a single aggregated payment per month across all their invoices + SELECT + supplier_id, + SUM(rate_amount) as payment_amount, + payment_date + +FROM invoice_payment_calendar +GROUP BY supplier_id, payment_date +), + +monthly_plan AS ( +-- Compute remaining balance after each monthly payment + SELECT + spa.supplier_id, + s.name AS supplier_name, + spa.payment_amount, + ROUND( + SUM(spa.payment_amount) OVER (PARTITION BY spa.supplier_id) + - SUM(spa.payment_amount) OVER ( + PARTITION BY spa.supplier_id + ORDER BY spa.payment_date + ), + 2 + ) AS balance_outstanding, + spa.payment_date + FROM supplier_payment_amount spa + JOIN supplier s + ON s.supplier_id = spa.supplier_id +) +SELECT + supplier_id, + supplier_name, + CAST(payment_amount AS DECIMAL (18,2)) as payment_amount, + CAST(balance_outstanding AS DECIMAL (18,2)) as balance_outstanding, + payment_date +FROM monthly_plan +ORDER BY supplier_id, payment_date; \ No newline at end of file