diff --git a/-v b/-v new file mode 100644 index 0000000..e69de29 diff --git a/calculate_largest_expensors.sql b/calculate_largest_expensors.sql index e69de29..db4fba0 100644 --- a/calculate_largest_expensors.sql +++ b/calculate_largest_expensors.sql @@ -0,0 +1,23 @@ +USE memory.default; + +SELECT + e.employee_id, + CONCAT(e.first_name, ' ', e.last_name) AS employee_name, + e.manager_id, + CONCAT(COALESCE(m.first_name, ''), ' ', COALESCE(m.last_name, '')) AS manager_name, + ROUND(SUM(exp.unit_price * exp.quantity), 2) AS total_expensed_amount +FROM + SEXI.EMPLOYEE e + LEFT JOIN SEXI.EMPLOYEE m ON e.manager_id = m.employee_id + INNER JOIN SEXI.EXPENSE exp ON e.employee_id = exp.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; diff --git a/create_employees.sql b/create_employees.sql index e69de29..bc48412 100644 --- a/create_employees.sql +++ b/create_employees.sql @@ -0,0 +1,33 @@ +-- Use the memory catalog and default schema +USE memory.default; + +-- Create the SEXI schema if it doesn't exist +CREATE SCHEMA IF NOT EXISTS SEXI; + +-- Drop the EMPLOYEE table if it exists +DROP TABLE IF EXISTS SEXI.EMPLOYEE; + +-- Create the EMPLOYEE table with data, casting employee_id and manager_id to TINYINT +CREATE TABLE SEXI.EMPLOYEE AS +SELECT + CAST(employee_id AS TINYINT) AS employee_id, + first_name, + last_name, + job_title, + CAST(manager_id AS TINYINT) AS manager_id +FROM +( + 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) +) AS t (employee_id, first_name, last_name, job_title, manager_id); + +-- Print out the result +SELECT * FROM SEXI.EMPLOYEE; diff --git a/create_expenses.sql b/create_expenses.sql index e69de29..d63b201 100644 --- a/create_expenses.sql +++ b/create_expenses.sql @@ -0,0 +1,34 @@ +USE memory.default; + +-- Create the SEXI schema if it doesn't exist +CREATE SCHEMA IF NOT EXISTS SEXI; + +-- Create the EXPENSE table if it doesn't exist +CREATE TABLE IF NOT EXISTS SEXI.EXPENSE ( + employee_id TINYINT, + unit_price DECIMAL(8, 2), + quantity TINYINT +); + +-- Insert data into SEXI.EXPENSE using the VALUES clause and an INNER JOIN +INSERT INTO SEXI.EXPENSE (employee_id, unit_price, quantity) +SELECT + emp.employee_id, + rec.unit_price, + rec.quantity +FROM + (VALUES + ('Alex', 'Jacobson', 6.50, 14), + ('Alex', 'Jacobson', 11.00, 20), + ('Alex', 'Jacobson', 22.00, 18), + ('Alex', 'Jacobson', 13.00, 75), + ('Andrea', 'Ghibaudi', 300.00, 1), + ('Darren', 'Poynton', 40.00, 9), + ('Umberto', 'Torrielli', 17.50, 4) + ) AS rec (first_name, last_name, unit_price, quantity) +INNER JOIN SEXI.EMPLOYEE emp + ON UPPER(emp.first_name) = UPPER(rec.first_name) + AND UPPER(emp.last_name) = UPPER(rec.last_name); + +-- Print SEXI.EXPENSE to check the solution +SELECT * FROM SEXI.EXPENSE; \ No newline at end of file diff --git a/create_invoices.sql b/create_invoices.sql index e69de29..7524f4c 100644 --- a/create_invoices.sql +++ b/create_invoices.sql @@ -0,0 +1,66 @@ +-- Use the memory catalog and default schema +USE memory.default; + +-- Create the SEXI schema if it doesn't exist +CREATE SCHEMA IF NOT EXISTS SEXI; + +-- Drop tables if they already exist +DROP TABLE IF EXISTS SEXI.SUPPLIER; +DROP TABLE IF EXISTS SEXI.INVOICE; + +-- Create the SUPPLIER table +CREATE TABLE SEXI.SUPPLIER ( + supplier_id TINYINT, + name VARCHAR +); + +-- Insert suppliers into the SUPPLIER table +INSERT INTO SEXI.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 +CREATE TABLE SEXI.INVOICE ( + supplier_id TINYINT, + invoice_amount DECIMAL(8, 2), + due_date DATE +); + +-- Base date for calculations (assuming current date is '2024-11-01') +-- We'll hardcode this date in each calculation + +-- Insert invoices into the INVOICE table +INSERT INTO SEXI.INVOICE (supplier_id, invoice_amount, due_date) +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Party Animals') AS supplier_id, + 6000.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '4' MONTH) - INTERVAL '1' DAY AS due_date +UNION ALL +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Catering Plus') AS supplier_id, + 2000.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '3' MONTH) - INTERVAL '1' DAY AS due_date +UNION ALL +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Catering Plus') AS supplier_id, + 1500.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '4' MONTH) - INTERVAL '1' DAY AS due_date +UNION ALL +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Dave''s Discos') AS supplier_id, + 500.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '2' MONTH) - INTERVAL '1' DAY AS due_date +UNION ALL +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Entertainment tonight') AS supplier_id, + 6000.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '4' MONTH) - INTERVAL '1' DAY AS due_date +UNION ALL +SELECT + (SELECT supplier_id FROM SEXI.SUPPLIER WHERE name = 'Ice Ice Baby') AS supplier_id, + 4000.00 AS invoice_amount, + date_trunc('month', DATE '2024-11-01' + INTERVAL '7' MONTH) - INTERVAL '1' DAY AS due_date; diff --git a/find_manager_cycles.sql b/find_manager_cycles.sql index e69de29..17b1a25 100644 --- a/find_manager_cycles.sql +++ b/find_manager_cycles.sql @@ -0,0 +1,35 @@ +USE memory.default; + +WITH RECURSIVE manager_cycles (employee_id, manager_id, path, visited) AS ( + -- Anchor member + SELECT + employee_id, + manager_id, + CAST(employee_id AS VARCHAR) AS path, + CAST(employee_id AS VARCHAR) AS visited + FROM + sexi.EMPLOYEE + + UNION ALL + + -- Recursive member + SELECT + mc.employee_id, + e.manager_id, + CONCAT(mc.path, '->', CAST(e.manager_id AS VARCHAR)) AS path, + CONCAT(mc.visited, ',', CAST(e.manager_id AS VARCHAR)) AS visited + FROM + manager_cycles mc + JOIN sexi.EMPLOYEE e ON mc.manager_id = e.employee_id + WHERE + POSITION(',' || CAST(e.manager_id AS VARCHAR) || ',' IN ',' || mc.visited || ',') = 0 + AND e.manager_id IS NOT NULL +) + +SELECT + employee_id AS cyclic_employee_id, + path AS cycle_path +FROM + manager_cycles +WHERE + POSITION(',' || CAST(manager_id AS VARCHAR) || ',' IN ',' || visited || ',') > 0; diff --git a/generate_supplier_payment_plans.sql b/generate_supplier_payment_plans.sql index e69de29..e0bf753 100644 --- a/generate_supplier_payment_plans.sql +++ b/generate_supplier_payment_plans.sql @@ -0,0 +1,118 @@ +-- Use the memory catalog and default schema +USE memory.default; + +-- Ensure the SEXI schema exists +CREATE SCHEMA IF NOT EXISTS SEXI; + + +-- Define the payment start date (first day of next month) +WITH payment_start AS ( + SELECT date_trunc('month', current_date + interval '1' month) AS payment_start_date +), + +-- Get the maximum due date from the invoices +max_due_date AS ( + SELECT MAX(due_date) AS max_due_date + FROM SEXI.INVOICE +), + +-- Generate a series of end-of-month dates from payment start date up to the maximum due date +end_of_months AS ( + SELECT date_trunc('month', date) + interval '1' month - interval '1' day AS end_of_month + FROM ( + SELECT sequence( + (SELECT payment_start_date FROM payment_start), + (SELECT max_due_date FROM max_due_date), + INTERVAL '1' MONTH + ) AS payment_dates + ) + CROSS JOIN UNNEST(payment_dates) AS t(date) +), + +-- Calculate the number of payments and monthly payment amounts for each invoice +invoice_payments AS ( + SELECT + inv.supplier_id, + s.name AS supplier_name, + inv.invoice_amount, + inv.due_date, + date_diff('month', (SELECT payment_start_date FROM payment_start) - INTERVAL '1' DAY, inv.due_date) AS months_till_payment, + inv.invoice_amount / date_diff('month', (SELECT payment_start_date FROM payment_start) - INTERVAL '1' DAY, inv.due_date) AS monthly_payment_raw + FROM SEXI.INVOICE inv + JOIN SEXI.SUPPLIER s ON inv.supplier_id = s.supplier_id +), + +-- Adjust monthly payments to handle rounding errors +adjusted_invoice_payments AS ( + SELECT + supplier_id, + supplier_name, + invoice_amount, + due_date, + months_till_payment, + ROUND(monthly_payment_raw, 2) AS monthly_payment, + invoice_amount - (ROUND(monthly_payment_raw, 2) * (months_till_payment - 1)) AS last_payment_amount + FROM invoice_payments +), + +-- Generate payment schedule for each invoice +invoice_payment_schedule AS ( + SELECT + aip.supplier_id, + aip.supplier_name, + aip.invoice_amount, + aip.due_date, + eom.end_of_month AS payment_date, + CASE + WHEN ROW_NUMBER() OVER (PARTITION BY aip.supplier_id, aip.invoice_amount ORDER BY eom.end_of_month) < aip.months_till_payment THEN aip.monthly_payment + ELSE aip.last_payment_amount + END AS payment_amount + FROM adjusted_invoice_payments aip + JOIN end_of_months eom ON eom.end_of_month <= aip.due_date + WHERE eom.end_of_month >= (SELECT payment_start_date FROM payment_start) +), + +-- Aggregate payments per supplier per payment date +supplier_payments AS ( + SELECT + supplier_id, + supplier_name, + payment_date, + SUM(payment_amount) AS payment_amount + FROM invoice_payment_schedule + GROUP BY supplier_id, supplier_name, payment_date +), + +-- Calculate balance outstanding per supplier +balance_outstanding AS ( + SELECT + supplier_id, + supplier_name, + SUM(invoice_amount) AS total_balance + FROM adjusted_invoice_payments + GROUP BY supplier_id, supplier_name +), + +-- Calculate cumulative payments per supplier to determine balance outstanding after each payment +cumulative_payments AS ( + SELECT + sp.supplier_id, + sp.supplier_name, + sp.payment_date, + sp.payment_amount, + bo.total_balance, + SUM(sp.payment_amount) OVER (PARTITION BY sp.supplier_id ORDER BY sp.payment_date) AS cumulative_payment, + bo.total_balance - SUM(sp.payment_amount) OVER (PARTITION BY sp.supplier_id ORDER BY sp.payment_date) AS balance_outstanding + FROM supplier_payments sp + JOIN balance_outstanding bo ON sp.supplier_id = bo.supplier_id +) + +-- Final Output +SELECT + supplier_id, + supplier_name, + ROUND(payment_amount, 2) AS payment_amount, + ROUND(balance_outstanding, 2) AS balance_outstanding, + payment_date +FROM cumulative_payments +ORDER BY supplier_id, payment_date;