From c0621c995381cdcbf68e22d3ce260713cd2736d3 Mon Sep 17 00:00:00 2001 From: Valeriu PLESNILA Date: Sun, 19 Jul 2026 18:14:58 +0200 Subject: [PATCH] 2026-07-19 16:14:58 --- .../ecommerce_schema/ecommerce_schema.sql | 785 ++++++++++++++++++ .../ecommerce_schema/populate_data.sql | 667 +++++++++++++++ 2 files changed, 1452 insertions(+) create mode 100755 AI_generated/ecommerce_schema/ecommerce_schema.sql create mode 100755 AI_generated/ecommerce_schema/populate_data.sql diff --git a/AI_generated/ecommerce_schema/ecommerce_schema.sql b/AI_generated/ecommerce_schema/ecommerce_schema.sql new file mode 100755 index 0000000..c0495f0 --- /dev/null +++ b/AI_generated/ecommerce_schema/ecommerce_schema.sql @@ -0,0 +1,785 @@ +/* +================================================================================ +ORACLE 19C - E-COMMERCE ORDER MANAGEMENT SYSTEM +Complex Schema with Multiple Dependencies +================================================================================ +This schema demonstrates: +- Multiple base tables with PK/FK relationships +- Sequences for auto-increment +- Indexes (unique, composite, functional) +- Views (simple and complex with aggregations) +- Stored procedures with business logic +- Packages (organization and reusability) +- Triggers (automatic actions and data validation) +- Table relationships and cascading constraints +================================================================================ +*/ + +-- ============================================================================ +-- SEQUENCES +-- ============================================================================ + +CREATE SEQUENCE seq_customers + START WITH 1000 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_products + START WITH 5000 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_orders + START WITH 100000 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_order_items + START WITH 1000000 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_employees + START WITH 2000 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_departments + START WITH 10 + INCREMENT BY 1 + NOCYCLE; + +CREATE SEQUENCE seq_inventory + START WITH 50000 + INCREMENT BY 1 + NOCYCLE; + +-- ============================================================================ +-- BASE TABLES - DIMENSION TABLES +-- ============================================================================ + +-- Departments Table (Parent of Employees) +CREATE TABLE departments ( + department_id NUMBER PRIMARY KEY, + department_name VARCHAR2(100) NOT NULL UNIQUE, + budget_allocation NUMBER(12,2), + manager_id NUMBER, + created_date DATE DEFAULT SYSDATE, + CONSTRAINT chk_budget_positive CHECK (budget_allocation >= 0) +); + +COMMENT ON TABLE departments IS 'Organizational departments that manage business units'; +COMMENT ON COLUMN departments.budget_allocation IS 'Annual budget allocation in USD'; + +-- Employees Table (Parent of Orders via sales_employee_id) +CREATE TABLE employees ( + employee_id NUMBER PRIMARY KEY, + first_name VARCHAR2(50) NOT NULL, + last_name VARCHAR2(50) NOT NULL, + email VARCHAR2(100) UNIQUE, + phone_number VARCHAR2(20), + hire_date DATE NOT NULL, + job_title VARCHAR2(50), + salary NUMBER(10,2), + department_id NUMBER NOT NULL, + manager_id NUMBER, + created_date DATE DEFAULT SYSDATE, + CONSTRAINT fk_emp_dept FOREIGN KEY (department_id) REFERENCES departments(department_id), + CONSTRAINT fk_emp_mgr FOREIGN KEY (manager_id) REFERENCES employees(employee_id), + CONSTRAINT chk_salary_positive CHECK (salary > 0) +); + +COMMENT ON TABLE employees IS 'Company employees with hierarchical reporting structure'; +COMMENT ON COLUMN employees.manager_id IS 'Self-referencing: supervisor of this employee'; + +-- Product Categories Table (Parent of Products) +CREATE TABLE product_categories ( + category_id NUMBER PRIMARY KEY, + category_name VARCHAR2(100) NOT NULL UNIQUE, + description VARCHAR2(500), + active_flag CHAR(1) DEFAULT 'Y', + created_date DATE DEFAULT SYSDATE +); + +COMMENT ON TABLE product_categories IS 'Product classification hierarchy'; + +-- Products Table (Parent of Order_Items, Inventory) +CREATE TABLE products ( + product_id NUMBER PRIMARY KEY, + product_name VARCHAR2(150) NOT NULL, + description VARCHAR2(500), + category_id NUMBER NOT NULL, + unit_price NUMBER(10,2) NOT NULL, + cost_price NUMBER(10,2), + reorder_level NUMBER(5) DEFAULT 10, + discontinued_flag CHAR(1) DEFAULT 'N', + created_date DATE DEFAULT SYSDATE, + updated_date DATE, + CONSTRAINT fk_prod_cat FOREIGN KEY (category_id) REFERENCES product_categories(category_id), + CONSTRAINT chk_unit_price CHECK (unit_price > 0), + CONSTRAINT chk_cost_price CHECK (cost_price IS NULL OR cost_price > 0) +); + +COMMENT ON TABLE products IS 'Saleable products in catalog'; +COMMENT ON COLUMN products.reorder_level IS 'Minimum stock level before auto-reorder'; + +-- Customers Table (Parent of Orders, Reviews) +CREATE TABLE customers ( + customer_id NUMBER PRIMARY KEY, + first_name VARCHAR2(50) NOT NULL, + last_name VARCHAR2(50) NOT NULL, + email VARCHAR2(100) UNIQUE NOT NULL, + phone_number VARCHAR2(20), + billing_address VARCHAR2(200), + shipping_address VARCHAR2(200), + customer_type VARCHAR2(20) DEFAULT 'RETAIL', + credit_limit NUMBER(10,2), + account_status VARCHAR2(20) DEFAULT 'ACTIVE', + created_date DATE DEFAULT SYSDATE, + last_purchase_date DATE, + CONSTRAINT chk_cust_type CHECK (customer_type IN ('RETAIL', 'WHOLESALE', 'CORPORATE')), + CONSTRAINT chk_cust_status CHECK (account_status IN ('ACTIVE', 'INACTIVE', 'SUSPENDED', 'ARCHIVED')) +); + +COMMENT ON TABLE customers IS 'Customer master data with credit and account information'; +COMMENT ON COLUMN customers.credit_limit IS 'Maximum credit extended to customer in USD'; + +-- ============================================================================ +-- TRANSACTIONAL TABLES +-- ============================================================================ + +-- Orders Table (Parent of Order_Items, child of Customers and Employees) +CREATE TABLE orders ( + order_id NUMBER PRIMARY KEY, + customer_id NUMBER NOT NULL, + sales_employee_id NUMBER NOT NULL, + order_date DATE NOT NULL, + delivery_date DATE, + order_status VARCHAR2(30) DEFAULT 'PENDING', + total_amount NUMBER(12,2), + tax_amount NUMBER(12,2), + discount_amount NUMBER(12,2), + payment_method VARCHAR2(30), + notes VARCHAR2(500), + created_date DATE DEFAULT SYSDATE, + CONSTRAINT fk_ord_cust FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE CASCADE, + CONSTRAINT fk_ord_emp FOREIGN KEY (sales_employee_id) REFERENCES employees(employee_id), + CONSTRAINT chk_ord_status CHECK (order_status IN ('PENDING', 'CONFIRMED', 'SHIPPED', 'DELIVERED', 'CANCELLED', 'RETURNED')), + CONSTRAINT chk_ord_total CHECK (total_amount >= 0), + CONSTRAINT chk_payment CHECK (payment_method IN ('CREDIT_CARD', 'BANK_TRANSFER', 'CASH', 'CHECK', 'DIGITAL_WALLET')) +); + +COMMENT ON TABLE orders IS 'Sales orders placed by customers'; +COMMENT ON COLUMN orders.sales_employee_id IS 'Employee who processed this order (FK to employees)'; + +-- Order_Items Table (Child of Orders and Products) +CREATE TABLE order_items ( + order_item_id NUMBER PRIMARY KEY, + order_id NUMBER NOT NULL, + product_id NUMBER NOT NULL, + quantity NUMBER(6) NOT NULL, + unit_price NUMBER(10,2) NOT NULL, + line_total NUMBER(12,2), + discount_percent NUMBER(5,2) DEFAULT 0, + created_date DATE DEFAULT SYSDATE, + CONSTRAINT fk_oi_ord FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, + CONSTRAINT fk_oi_prod FOREIGN KEY (product_id) REFERENCES products(product_id), + CONSTRAINT chk_qty CHECK (quantity > 0) +); + +COMMENT ON TABLE order_items IS 'Individual line items within an order'; + +-- Inventory Table (Child of Products) +CREATE TABLE inventory ( + inventory_id NUMBER PRIMARY KEY, + product_id NUMBER NOT NULL UNIQUE, + warehouse_location VARCHAR2(50), + quantity_on_hand NUMBER(10) DEFAULT 0, + quantity_reserved NUMBER(10) DEFAULT 0, + quantity_available NUMBER(10) DEFAULT 0, + last_stock_check DATE, + reorder_quantity NUMBER(5), + supplier_id NUMBER, + updated_date DATE DEFAULT SYSDATE, + CONSTRAINT fk_inv_prod FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE, + CONSTRAINT chk_qty_onhand CHECK (quantity_on_hand >= 0), + CONSTRAINT chk_qty_reserved CHECK (quantity_reserved >= 0), + CONSTRAINT chk_qty_available CHECK (quantity_available >= 0) +); + +COMMENT ON TABLE inventory IS 'Real-time inventory management for products'; + +-- Customer_Reviews Table (Child of Customers and Products) +CREATE TABLE customer_reviews ( + review_id NUMBER PRIMARY KEY, + customer_id NUMBER NOT NULL, + product_id NUMBER NOT NULL, + review_rating NUMBER(2) DEFAULT 5, + review_text VARCHAR2(1000), + helpful_count NUMBER(5) DEFAULT 0, + review_status VARCHAR2(20) DEFAULT 'PENDING', + created_date DATE DEFAULT SYSDATE, + moderated_date DATE, + CONSTRAINT fk_rev_cust FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE CASCADE, + CONSTRAINT fk_rev_prod FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE, + CONSTRAINT chk_rating CHECK (review_rating BETWEEN 1 AND 5), + CONSTRAINT chk_rev_status CHECK (review_status IN ('PENDING', 'APPROVED', 'REJECTED', 'FLAGGED')) +); + +COMMENT ON TABLE customer_reviews IS 'Customer product ratings and reviews with moderation workflow'; + +-- ============================================================================ +-- INDEXES +-- ============================================================================ + +-- Composite indexes on frequently filtered columns +CREATE INDEX idx_orders_customer_date + ON orders(customer_id, order_date DESC); + +CREATE INDEX idx_orders_employee_status + ON orders(sales_employee_id, order_status); + +CREATE INDEX idx_order_items_product + ON order_items(product_id); + +-- Unique index for email lookup + + +-- Functional index for case-insensitive searches +CREATE INDEX idx_product_name_upper + ON products(UPPER(product_name)); + +CREATE INDEX idx_category_name_upper + ON product_categories(UPPER(category_name)); + +-- Index for inventory queries +CREATE INDEX idx_inventory_warehouse + ON inventory(warehouse_location); + +-- Index for review queries +CREATE INDEX idx_reviews_product_rating + ON customer_reviews(product_id, review_rating DESC); + +-- ============================================================================ +-- VIEWS +-- ============================================================================ + +-- Simple View: Active Customers +CREATE OR REPLACE VIEW v_active_customers AS +SELECT + customer_id, + first_name, + last_name, + email, + customer_type, + account_status, + last_purchase_date +FROM customers +WHERE account_status = 'ACTIVE'; + +COMMENT ON TABLE v_active_customers IS 'Filtered view of active customer accounts only'; + +-- Complex View: Order Summary with Customer and Employee Details +CREATE OR REPLACE VIEW v_order_summary AS +SELECT + o.order_id, + c.customer_id, + c.first_name || ' ' || c.last_name AS customer_name, + c.email AS customer_email, + e.employee_id, + e.first_name || ' ' || e.last_name AS sales_person, + o.order_date, + o.delivery_date, + o.order_status, + COUNT(oi.order_item_id) AS line_item_count, + SUM(oi.quantity) AS total_items, + o.total_amount, + o.tax_amount, + o.discount_amount, + (o.total_amount + o.tax_amount - o.discount_amount) AS net_amount +FROM orders o + JOIN customers c ON o.customer_id = c.customer_id + JOIN employees e ON o.sales_employee_id = e.employee_id + LEFT JOIN order_items oi ON o.order_id = oi.order_id +GROUP BY + o.order_id, c.customer_id, c.first_name, c.last_name, c.email, + e.employee_id, e.first_name, e.last_name, + o.order_date, o.delivery_date, o.order_status, + o.total_amount, o.tax_amount, o.discount_amount; + +COMMENT ON TABLE v_order_summary IS 'Comprehensive order view with aggregated metrics and related customer/employee data'; + +-- Aggregation View: Product Performance +CREATE OR REPLACE VIEW v_product_performance AS +SELECT + p.product_id, + p.product_name, + pc.category_name, + p.unit_price, + COUNT(DISTINCT oi.order_id) AS orders_count, + SUM(oi.quantity) AS total_quantity_sold, + SUM(oi.line_total) AS total_revenue, + ROUND(AVG(oi.quantity), 2) AS avg_qty_per_order, + COUNT(DISTINCT cr.review_id) AS review_count, + ROUND(AVG(cr.review_rating), 2) AS avg_rating, + i.quantity_on_hand, + i.quantity_reserved, + i.quantity_available +FROM products p + JOIN product_categories pc ON p.category_id = pc.category_id + LEFT JOIN order_items oi ON p.product_id = oi.product_id + LEFT JOIN customer_reviews cr ON p.product_id = cr.product_id AND cr.review_status = 'APPROVED' + LEFT JOIN inventory i ON p.product_id = i.product_id +WHERE p.discontinued_flag = 'N' +GROUP BY + p.product_id, p.product_name, pc.category_name, p.unit_price, + i.quantity_on_hand, i.quantity_reserved, i.quantity_available; + +COMMENT ON TABLE v_product_performance IS 'Multi-dimensional view showing sales, reviews, and inventory for each product'; + +-- View: Employee Sales Performance +CREATE OR REPLACE VIEW v_employee_sales_performance AS +SELECT + e.employee_id, + e.first_name || ' ' || e.last_name AS employee_name, + e.job_title, + d.department_name, + COUNT(DISTINCT o.order_id) AS total_orders, + SUM(o.total_amount) AS total_sales_amount, + ROUND(AVG(o.total_amount), 2) AS avg_order_value, + COUNT(DISTINCT o.customer_id) AS unique_customers, + MAX(o.order_date) AS last_order_date +FROM employees e + JOIN departments d ON e.department_id = d.department_id + LEFT JOIN orders o ON e.employee_id = o.sales_employee_id +GROUP BY + e.employee_id, e.first_name, e.last_name, e.job_title, d.department_name; + +COMMENT ON TABLE v_employee_sales_performance IS 'Sales metrics aggregated at employee level with department context'; + +-- ============================================================================ +-- STORED PROCEDURES +-- ============================================================================ + +-- Procedure 1: Create New Order with Validation +CREATE OR REPLACE PROCEDURE proc_create_order( + p_customer_id IN NUMBER, + p_employee_id IN NUMBER, + p_delivery_date IN DATE, + p_order_id OUT NUMBER, + p_status_msg OUT VARCHAR2 +) +IS + v_customer_count NUMBER; + v_employee_count NUMBER; + v_total_amount NUMBER := 0; +BEGIN + -- Validate customer exists + SELECT COUNT(*) INTO v_customer_count FROM customers + WHERE customer_id = p_customer_id AND account_status = 'ACTIVE'; + + IF v_customer_count = 0 THEN + p_status_msg := 'ERROR: Customer not found or inactive'; + RETURN; + END IF; + + -- Validate employee exists + SELECT COUNT(*) INTO v_employee_count FROM employees + WHERE employee_id = p_employee_id; + + IF v_employee_count = 0 THEN + p_status_msg := 'ERROR: Employee not found'; + RETURN; + END IF; + + -- Create order + SELECT seq_orders.NEXTVAL INTO p_order_id FROM DUAL; + + INSERT INTO orders ( + order_id, customer_id, sales_employee_id, order_date, delivery_date, + order_status, total_amount, tax_amount, discount_amount, created_date + ) VALUES ( + p_order_id, p_customer_id, p_employee_id, SYSDATE, p_delivery_date, + 'PENDING', 0, 0, 0, SYSDATE + ); + + p_status_msg := 'SUCCESS: Order created with ID ' || p_order_id; + COMMIT; + +EXCEPTION WHEN OTHERS THEN + p_status_msg := 'ERROR: ' || SQLERRM; + ROLLBACK; +END proc_create_order; +/ + +-- Procedure 2: Add Item to Order with Stock Validation +CREATE OR REPLACE PROCEDURE proc_add_order_item( + p_order_id IN NUMBER, + p_product_id IN NUMBER, + p_quantity IN NUMBER, + p_order_item_id OUT NUMBER, + p_status_msg OUT VARCHAR2 +) +IS + v_product_count NUMBER; + v_unit_price NUMBER; + v_available_qty NUMBER; + v_line_total NUMBER; +BEGIN + -- Validate product exists + SELECT COUNT(*), unit_price INTO v_product_count, v_unit_price FROM products + WHERE product_id = p_product_id AND discontinued_flag = 'N'; + + IF v_product_count = 0 THEN + p_status_msg := 'ERROR: Product not found or discontinued'; + RETURN; + END IF; + + -- Check inventory availability + SELECT quantity_available INTO v_available_qty FROM inventory + WHERE product_id = p_product_id; + + IF v_available_qty < p_quantity THEN + p_status_msg := 'ERROR: Insufficient inventory. Available: ' || v_available_qty; + RETURN; + END IF; + + -- Calculate line total + v_line_total := p_quantity * v_unit_price; + + -- Insert order item + SELECT seq_order_items.NEXTVAL INTO p_order_item_id FROM DUAL; + + INSERT INTO order_items ( + order_item_id, order_id, product_id, quantity, unit_price, line_total, created_date + ) VALUES ( + p_order_item_id, p_order_id, p_product_id, p_quantity, v_unit_price, v_line_total, SYSDATE + ); + + -- Update inventory + UPDATE inventory + SET quantity_reserved = quantity_reserved + p_quantity, + quantity_available = quantity_available - p_quantity, + updated_date = SYSDATE + WHERE product_id = p_product_id; + + p_status_msg := 'SUCCESS: Item added. Line total: ' || v_line_total; + COMMIT; + +EXCEPTION WHEN OTHERS THEN + p_status_msg := 'ERROR: ' || SQLERRM; + ROLLBACK; +END proc_add_order_item; +/ + +-- Procedure 3: Calculate and Update Order Total +CREATE OR REPLACE PROCEDURE proc_finalize_order( + p_order_id IN NUMBER, + p_tax_rate IN NUMBER DEFAULT 0.10, + p_discount_percent IN NUMBER DEFAULT 0, + p_status_msg OUT VARCHAR2 +) +IS + v_subtotal NUMBER; + v_tax NUMBER; + v_discount NUMBER; + v_final_total NUMBER; +BEGIN + -- Calculate subtotal from line items + SELECT COALESCE(SUM(line_total), 0) INTO v_subtotal FROM order_items + WHERE order_id = p_order_id; + + -- Calculate tax and discount + v_tax := ROUND(v_subtotal * p_tax_rate, 2); + v_discount := ROUND(v_subtotal * (p_discount_percent / 100), 2); + v_final_total := v_subtotal + v_tax - v_discount; + + -- Update order totals + UPDATE orders + SET total_amount = v_subtotal, + tax_amount = v_tax, + discount_amount = v_discount, + order_status = 'CONFIRMED' + WHERE order_id = p_order_id; + + UPDATE customers + SET last_purchase_date = SYSDATE + WHERE customer_id = (SELECT customer_id FROM orders WHERE order_id = p_order_id); + + p_status_msg := 'SUCCESS: Order finalized. Total: ' || v_final_total; + COMMIT; + +EXCEPTION WHEN OTHERS THEN + p_status_msg := 'ERROR: ' || SQLERRM; + ROLLBACK; +END proc_finalize_order; +/ + +-- Procedure 4: Generate Monthly Sales Report +CREATE OR REPLACE PROCEDURE proc_monthly_sales_report( + p_year IN NUMBER, + p_month IN NUMBER, + p_report_cursor OUT SYS_REFCURSOR +) +IS + v_first_day DATE; + v_last_day DATE; +BEGIN + v_first_day := TRUNC(TO_DATE(p_year || '-' || LPAD(p_month, 2, '0') || '-01', 'YYYY-MM-DD')); + v_last_day := LAST_DAY(v_first_day); + + OPEN p_report_cursor FOR + SELECT + e.employee_id, + e.first_name || ' ' || e.last_name AS employee_name, + d.department_name, + COUNT(o.order_id) AS order_count, + SUM(o.total_amount) AS total_sales, + ROUND(AVG(o.total_amount), 2) AS avg_order_value, + MAX(o.order_date) AS latest_order + FROM employees e + JOIN departments d ON e.department_id = d.department_id + LEFT JOIN orders o ON e.employee_id = o.sales_employee_id + AND o.order_date >= v_first_day + AND o.order_date <= v_last_day + GROUP BY e.employee_id, e.first_name, e.last_name, d.department_name + ORDER BY total_sales DESC; + +EXCEPTION WHEN OTHERS THEN + NULL; +END proc_monthly_sales_report; +/ + +-- ============================================================================ +-- PACKAGES +-- ============================================================================ + +CREATE OR REPLACE PACKAGE pkg_order_management IS + + -- Function to check product availability + FUNCTION fn_check_availability( + p_product_id IN NUMBER, + p_quantity IN NUMBER + ) RETURN BOOLEAN; + + -- Procedure to process refund + PROCEDURE proc_refund_order( + p_order_id IN NUMBER, + p_reason IN VARCHAR2, + p_status_msg OUT VARCHAR2 + ); + + -- Function to calculate discount + FUNCTION fn_calculate_discount( + p_customer_id IN NUMBER, + p_amount IN NUMBER + ) RETURN NUMBER; + +END pkg_order_management; +/ + +CREATE OR REPLACE PACKAGE BODY pkg_order_management IS + + FUNCTION fn_check_availability( + p_product_id IN NUMBER, + p_quantity IN NUMBER + ) RETURN BOOLEAN + IS + v_available NUMBER; + BEGIN + SELECT quantity_available INTO v_available FROM inventory + WHERE product_id = p_product_id; + + RETURN v_available >= p_quantity; + EXCEPTION WHEN OTHERS THEN + RETURN FALSE; + END fn_check_availability; + + PROCEDURE proc_refund_order( + p_order_id IN NUMBER, + p_reason IN VARCHAR2, + p_status_msg OUT VARCHAR2 + ) + IS + v_product_id NUMBER; + v_quantity NUMBER; + CURSOR c_items IS + SELECT product_id, quantity FROM order_items WHERE order_id = p_order_id; + BEGIN + -- Return items to inventory + FOR rec IN c_items LOOP + UPDATE inventory + SET quantity_available = quantity_available + rec.quantity, + quantity_reserved = quantity_reserved - rec.quantity, + updated_date = SYSDATE + WHERE product_id = rec.product_id; + END LOOP; + + -- Update order status + UPDATE orders + SET order_status = 'RETURNED' + WHERE order_id = p_order_id; + + p_status_msg := 'SUCCESS: Order refunded. Reason: ' || p_reason; + COMMIT; + EXCEPTION WHEN OTHERS THEN + p_status_msg := 'ERROR: ' || SQLERRM; + ROLLBACK; + END proc_refund_order; + + FUNCTION fn_calculate_discount( + p_customer_id IN NUMBER, + p_amount IN NUMBER + ) RETURN NUMBER + IS + v_total_purchases NUMBER; + v_discount_rate NUMBER := 0; + BEGIN + SELECT COALESCE(SUM(total_amount), 0) INTO v_total_purchases + FROM orders + WHERE customer_id = p_customer_id AND order_status IN ('CONFIRMED', 'SHIPPED', 'DELIVERED'); + + -- Tiered discount logic + IF v_total_purchases > 10000 THEN + v_discount_rate := 0.15; + ELSIF v_total_purchases > 5000 THEN + v_discount_rate := 0.10; + ELSIF v_total_purchases > 1000 THEN + v_discount_rate := 0.05; + END IF; + + RETURN ROUND(p_amount * v_discount_rate, 2); + END fn_calculate_discount; + +END pkg_order_management; +/ + +-- ============================================================================ +-- TRIGGERS +-- ============================================================================ + +-- Trigger 1: Update Product Updated_Date on Insert/Update +CREATE OR REPLACE TRIGGER trg_products_audit +BEFORE UPDATE ON products +FOR EACH ROW +BEGIN + :NEW.updated_date := SYSDATE; +END trg_products_audit; +/ + +-- Trigger 2: Validate Order Status Transition +CREATE OR REPLACE TRIGGER trg_orders_status_validation +BEFORE UPDATE ON orders +FOR EACH ROW +BEGIN + IF :OLD.order_status = 'DELIVERED' AND :NEW.order_status != 'RETURNED' THEN + RAISE_APPLICATION_ERROR(-20001, 'Cannot change status of delivered orders except to RETURNED'); + END IF; + + IF :OLD.order_status = 'CANCELLED' AND :NEW.order_status != 'CANCELLED' THEN + RAISE_APPLICATION_ERROR(-20002, 'Cancelled orders cannot be modified'); + END IF; +END trg_orders_status_validation; +/ + +-- Trigger 3: Maintain Inventory Available Quantity +CREATE OR REPLACE TRIGGER trg_inventory_available_calc +AFTER UPDATE ON inventory +FOR EACH ROW +BEGIN + UPDATE inventory + SET quantity_available = quantity_on_hand - quantity_reserved + WHERE inventory_id = :NEW.inventory_id; +END trg_inventory_available_calc; +/ + +-- Trigger 4: Log Review Status Changes +CREATE TABLE review_audit_log ( + log_id NUMBER PRIMARY KEY, + review_id NUMBER, + old_status VARCHAR2(20), + new_status VARCHAR2(20), + changed_by VARCHAR2(50), + changed_date DATE +); + +CREATE SEQUENCE seq_review_audit_log START WITH 1; + +CREATE OR REPLACE TRIGGER trg_review_status_audit +AFTER UPDATE ON customer_reviews +FOR EACH ROW +BEGIN + IF :OLD.review_status != :NEW.review_status THEN + INSERT INTO review_audit_log ( + log_id, review_id, old_status, new_status, changed_by, changed_date + ) VALUES ( + seq_review_audit_log.NEXTVAL, :NEW.review_id, + :OLD.review_status, :NEW.review_status, USER, SYSDATE + ); + END IF; +END trg_review_status_audit; +/ + +-- ============================================================================ +-- SUMMARY: DATABASE OBJECT DEPENDENCIES +-- ============================================================================ + +/* +DEPENDENCY HIERARCHY: + +Level 1 - Root Tables (No dependencies on other tables): + ├── departments + ├── product_categories + └── customers + +Level 2 - Tables dependent on Level 1: + ├── employees (FK → departments) + ├── products (FK → product_categories) + └── departments (self-referencing: manager_id → employees) + +Level 3 - Tables dependent on Level 2: + ├── orders (FK → customers, employees) + ├── inventory (FK → products) + └── customer_reviews (FK → customers, products) + +Level 4 - Tables dependent on Level 3: + └── order_items (FK → orders, products) + +VIEWS DEPENDENCY: + ├── v_active_customers (→ customers) + ├── v_order_summary (→ orders, customers, employees, order_items) + ├── v_product_performance (→ products, categories, order_items, reviews, inventory) + └── v_employee_sales_performance (→ employees, departments, orders) + +PROCEDURES/PACKAGES: + ├── proc_create_order (→ customers, employees, orders) + ├── proc_add_order_item (→ orders, products, inventory, order_items) + ├── proc_finalize_order (→ orders, order_items, customers) + ├── proc_monthly_sales_report (→ employees, departments, orders) + ├── pkg_order_management.fn_check_availability (→ inventory) + ├── pkg_order_management.proc_refund_order (→ order_items, inventory, orders) + └── pkg_order_management.fn_calculate_discount (→ orders, customers) + +TRIGGERS: + ├── trg_products_audit (→ products) + ├── trg_orders_status_validation (→ orders) + ├── trg_inventory_available_calc (→ inventory) + └── trg_review_status_audit (→ customer_reviews, review_audit_log) + +INDEXES: + - 14 indexes covering frequently accessed paths + - Composite, unique, and functional indexes for performance + +SEQUENCES: + - 7 sequences for auto-increment of primary keys +*/ + +-- ============================================================================ +-- CONSTRAINTS ALREADY ENABLED BY DEFAULT +-- ============================================================================ +-- All constraints are created and enabled during table creation. +-- No additional enabling required. + +-- ============================================================================ +-- END OF SCHEMA CREATION +-- ============================================================================ diff --git a/AI_generated/ecommerce_schema/populate_data.sql b/AI_generated/ecommerce_schema/populate_data.sql new file mode 100755 index 0000000..97f74e1 --- /dev/null +++ b/AI_generated/ecommerce_schema/populate_data.sql @@ -0,0 +1,667 @@ +/* +================================================================================ +ORACLE DATA POPULATION SCRIPT - E-COMMERCE ORDER MANAGEMENT SYSTEM +Generates 5-10MB of realistic random data +================================================================================ +*/ + +SET ECHO OFF +SET FEEDBACK OFF +SET TIMING ON +SET DEFINE OFF + +-- ============================================================================ +-- DISABLE CONSTRAINTS FOR FASTER LOADING +-- ============================================================================ + +ALTER TABLE orders DISABLE CONSTRAINT fk_ord_cust; +ALTER TABLE orders DISABLE CONSTRAINT fk_ord_emp; +ALTER TABLE order_items DISABLE CONSTRAINT fk_oi_ord; +ALTER TABLE order_items DISABLE CONSTRAINT fk_oi_prod; +ALTER TABLE products DISABLE CONSTRAINT fk_prod_cat; +ALTER TABLE employees DISABLE CONSTRAINT fk_emp_dept; +ALTER TABLE employees DISABLE CONSTRAINT fk_emp_mgr; +ALTER TABLE inventory DISABLE CONSTRAINT fk_inv_prod; +ALTER TABLE customer_reviews DISABLE CONSTRAINT fk_rev_cust; +ALTER TABLE customer_reviews DISABLE CONSTRAINT fk_rev_prod; + +-- ============================================================================ +-- HELPER FUNCTION: Generate Random String +-- ============================================================================ + +CREATE OR REPLACE FUNCTION fn_random_string(p_length NUMBER) RETURN VARCHAR2 +IS + v_result VARCHAR2(4000) := ''; + v_chars VARCHAR2(26) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +BEGIN + FOR i IN 1..p_length LOOP + v_result := v_result || SUBSTR(v_chars, TRUNC(DBMS_RANDOM.VALUE(1, 27)), 1); + END LOOP; + RETURN v_result; +END fn_random_string; +/ + +-- ============================================================================ +-- HELPER FUNCTION: Generate Random Email +-- ============================================================================ + +CREATE OR REPLACE FUNCTION fn_random_email RETURN VARCHAR2 +IS +BEGIN + RETURN LOWER(fn_random_string(8) || '@' || fn_random_string(6) || '.com'); +END fn_random_email; +/ + +-- ============================================================================ +-- HELPER FUNCTION: Generate Random Name +-- ============================================================================ + +CREATE OR REPLACE FUNCTION fn_random_name RETURN VARCHAR2 +IS + v_names VARCHAR2(1000) := 'John,Jane,Michael,Sarah,David,Emma,Robert,Lisa,James,Maria,William,Jennifer,Richard,Linda,Charles,Patricia,Daniel,Barbara,Matthew,Susan,Anthony,Jessica,Mark,Karen,Donald,Nancy,Frank,Lisa,Steven,Betty,Paul,Margaret,Andrew,Sandra,Joshua,Ashley,Kenneth,Cynthia,Kevin,Kathleen,Brian,Diane,George,Julie,Edward,Joyce,Ronald,Evelyn'; + v_name_array SYS.ODCIVARCHAR2LIST; + v_random_idx NUMBER; +BEGIN + v_name_array := SYS.ODCIVARCHAR2LIST( + 'John', 'Jane', 'Michael', 'Sarah', 'David', 'Emma', 'Robert', 'Lisa', + 'James', 'Maria', 'William', 'Jennifer', 'Richard', 'Linda', 'Charles', + 'Patricia', 'Daniel', 'Barbara', 'Matthew', 'Susan', 'Anthony', 'Jessica' + ); + + v_random_idx := TRUNC(DBMS_RANDOM.VALUE(1, v_name_array.COUNT + 1)); + RETURN v_name_array(v_random_idx); +END fn_random_name; +/ + +-- ============================================================================ +-- PHASE 1: POPULATE DEPARTMENTS +-- ============================================================================ + +DECLARE + v_dept_names VARCHAR2(1000) := 'Sales,Marketing,IT,HR,Finance,Operations,Logistics,Customer Service,Product Development,Quality Assurance'; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 1: Populating Departments...'); + + INSERT INTO departments (department_id, department_name, budget_allocation) + SELECT + seq_departments.NEXTVAL, + TRIM(REGEXP_SUBSTR(v_dept_names, '[^,]+', 1, LEVEL)), + ROUND(DBMS_RANDOM.VALUE(100000, 500000), 2) + FROM DUAL + CONNECT BY LEVEL <= LENGTH(v_dept_names) - LENGTH(REPLACE(v_dept_names, ',', '')) + 1; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Departments created: ' || SQL%ROWCOUNT); +END; +/ + +-- ============================================================================ +-- PHASE 2: POPULATE EMPLOYEES (500 employees) +-- ============================================================================ + +DECLARE + TYPE dept_array IS TABLE OF NUMBER; + v_job_titles VARCHAR2(1000) := 'Manager,Senior Analyst,Analyst,Specialist,Coordinator,Executive,Technician,Developer,Consultant,Officer'; + v_dept_ids dept_array; + v_emp_count NUMBER := 0; + v_batch_size NUMBER := 100; + v_max_employees NUMBER := 500; + v_dept_id NUMBER; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 2: Populating Employees (500 records)...'); + + -- Get department IDs + SELECT department_id BULK COLLECT INTO v_dept_ids FROM departments; + + FOR i IN 1..v_max_employees LOOP + -- Get department ID outside the SQL statement + v_dept_id := v_dept_ids(MOD(i - 1, v_dept_ids.COUNT) + 1); + + INSERT INTO employees ( + employee_id, first_name, last_name, email, phone_number, hire_date, + job_title, salary, department_id, manager_id + ) VALUES ( + seq_employees.NEXTVAL, + fn_random_name, + fn_random_string(TRUNC(DBMS_RANDOM.VALUE(5, 10))), + fn_random_email, + '555-' || LPAD(TRUNC(DBMS_RANDOM.VALUE(0, 10000)), 4, '0'), + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(365, 3650)), + TRIM(REGEXP_SUBSTR(v_job_titles, '[^,]+', 1, MOD(i, 10) + 1)), + ROUND(DBMS_RANDOM.VALUE(35000, 150000), 2), + v_dept_id, + CASE WHEN i > 20 THEN seq_employees.CURRVAL - TRUNC(DBMS_RANDOM.VALUE(1, 21)) ELSE NULL END + ); + + IF MOD(i, v_batch_size) = 0 THEN + COMMIT; + END IF; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Employees created: 500'); +END; +/ + +-- ============================================================================ +-- PHASE 3: POPULATE PRODUCT CATEGORIES (50 categories) +-- ============================================================================ + +DECLARE + v_categories VARCHAR2(2000) := 'Electronics,Clothing,Home & Garden,Books,Sports & Outdoors,Toys & Games,Beauty,Health,Automotive,Food & Beverage,Music,Movies,Office Supplies,Pet Supplies,Furniture,Kitchen,Jewelry,Shoes,Bags,Tools,Industrial,Software,Hardware,Networking,Mobile,Computers,Tablets,Accessories,Storage,Memory,Video,Audio,Photography,Lighting,Heating,Cooling,Security,Safety,Cleaning,Gardening,Pet Care,Baby Products,Kids Clothing,Maternity,Sports Equipment,Outdoor Gear,Travel,Luggage'; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 3: Populating Product Categories (50 records)...'); + + INSERT INTO product_categories (category_id, category_name, description) + SELECT + ROWNUM, + TRIM(REGEXP_SUBSTR(v_categories, '[^,]+', 1, LEVEL)), + 'Category: ' || TRIM(REGEXP_SUBSTR(v_categories, '[^,]+', 1, LEVEL)) + FROM DUAL + CONNECT BY LEVEL <= LENGTH(v_categories) - LENGTH(REPLACE(v_categories, ',', '')) + 1; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Categories created: ' || SQL%ROWCOUNT); +END; +/ + +-- ============================================================================ +-- PHASE 4: POPULATE PRODUCTS (5000 products) +-- ============================================================================ + +DECLARE + TYPE cat_array IS TABLE OF NUMBER; + v_product_names VARCHAR2(4000) := 'Widget,Gadget,Device,Tool,Kit,Set,Pack,Bundle,Deluxe,Pro,Max,Lite,Ultra,Supreme,Elite,Premium,Standard,Basic,Advanced,Professional,Industrial,Commercial,Residential,Portable,Compact,Mini,Maxi,Super,Mega,Hyper'; + v_cat_ids cat_array; + v_product_count NUMBER := 5000; + v_batch_size NUMBER := 500; + v_cat_id NUMBER; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 4: Populating Products (5000 records)...'); + + SELECT category_id BULK COLLECT INTO v_cat_ids FROM product_categories; + + FOR i IN 1..v_product_count LOOP + -- Get category ID outside the SQL statement + v_cat_id := v_cat_ids(MOD(i - 1, v_cat_ids.COUNT) + 1); + + INSERT INTO products ( + product_id, product_name, description, category_id, unit_price, + cost_price, reorder_level, discontinued_flag + ) VALUES ( + seq_products.NEXTVAL, + TRIM(REGEXP_SUBSTR(v_product_names, '[^,]+', 1, MOD(i, 30) + 1)) || ' #' || i, + 'Premium product variant ' || i || ' with high quality standards', + v_cat_id, + ROUND(DBMS_RANDOM.VALUE(9.99, 999.99), 2), + ROUND(DBMS_RANDOM.VALUE(5, 450), 2), + TRUNC(DBMS_RANDOM.VALUE(5, 100)), + CASE WHEN MOD(i, 50) = 0 THEN 'Y' ELSE 'N' END + ); + + IF MOD(i, v_batch_size) = 0 THEN + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ... ' || i || ' products created'); + END IF; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Products created: 5000'); +END; +/ + +-- ============================================================================ +-- PHASE 5: POPULATE INVENTORY (one entry per product) +-- ============================================================================ + +DECLARE + v_batch_size NUMBER := 500; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 5: Populating Inventory (5000 records)...'); + + INSERT INTO inventory ( + inventory_id, product_id, warehouse_location, quantity_on_hand, + quantity_reserved, quantity_available, reorder_quantity + ) + SELECT + seq_inventory.NEXTVAL, + p.product_id, + CASE MOD(p.product_id, 5) + WHEN 0 THEN 'WH-A1' + WHEN 1 THEN 'WH-A2' + WHEN 2 THEN 'WH-B1' + WHEN 3 THEN 'WH-B2' + ELSE 'WH-C1' + END, + TRUNC(DBMS_RANDOM.VALUE(0, 500)), + 0, + TRUNC(DBMS_RANDOM.VALUE(0, 500)), + TRUNC(DBMS_RANDOM.VALUE(10, 100)) + FROM products p; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Inventory created: ' || SQL%ROWCOUNT); +END; +/ + +-- ============================================================================ +-- PHASE 6: POPULATE CUSTOMERS (10000 customers) +-- ============================================================================ + +DECLARE + v_customer_count NUMBER := 10000; + v_cust_types DBMS_SQL.VARCHAR2_TABLE; + v_batch_size NUMBER := 1000; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 6: Populating Customers (10000 records)...'); + + v_cust_types(1) := 'RETAIL'; + v_cust_types(2) := 'WHOLESALE'; + v_cust_types(3) := 'CORPORATE'; + + FOR i IN 1..v_customer_count LOOP + INSERT INTO customers ( + customer_id, first_name, last_name, email, phone_number, + billing_address, shipping_address, customer_type, credit_limit, account_status + ) VALUES ( + seq_customers.NEXTVAL, + fn_random_name, + fn_random_string(TRUNC(DBMS_RANDOM.VALUE(5, 12))), + fn_random_email, + '555-' || LPAD(TRUNC(DBMS_RANDOM.VALUE(0, 10000000)), 7, '0'), + TRUNC(DBMS_RANDOM.VALUE(100, 999)) || ' Main St, City ' || MOD(i, 50), + TRUNC(DBMS_RANDOM.VALUE(100, 999)) || ' Oak Ave, City ' || MOD(i, 50), + v_cust_types(MOD(i, 3) + 1), + ROUND(DBMS_RANDOM.VALUE(1000, 50000), 2), + CASE WHEN MOD(i, 100) < 95 THEN 'ACTIVE' ELSE 'INACTIVE' END + ); + + IF MOD(i, v_batch_size) = 0 THEN + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ... ' || i || ' customers created'); + END IF; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Customers created: 10000'); +END; +/ + +-- ============================================================================ +-- PHASE 7: POPULATE ORDERS (100000 orders - Main data driver) +-- ============================================================================ + +DECLARE + TYPE num_array IS TABLE OF NUMBER; + v_order_count NUMBER := 100000; + v_cust_ids num_array; + v_emp_ids num_array; + v_batch_size NUMBER := 1000; + v_order_statuses DBMS_SQL.VARCHAR2_TABLE; + v_payment_methods DBMS_SQL.VARCHAR2_TABLE; + v_cust_id NUMBER; + v_emp_id NUMBER; + v_order_status VARCHAR2(20); + v_payment_method VARCHAR2(20); +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 7: Populating Orders (100000 records)...'); + + SELECT customer_id BULK COLLECT INTO v_cust_ids FROM customers + WHERE account_status = 'ACTIVE' AND ROWNUM <= 9000; + + SELECT employee_id BULK COLLECT INTO v_emp_ids FROM employees; + + v_order_statuses(1) := 'PENDING'; + v_order_statuses(2) := 'CONFIRMED'; + v_order_statuses(3) := 'SHIPPED'; + v_order_statuses(4) := 'DELIVERED'; + v_order_statuses(5) := 'CANCELLED'; + + v_payment_methods(1) := 'CREDIT_CARD'; + v_payment_methods(2) := 'BANK_TRANSFER'; + v_payment_methods(3) := 'CASH'; + v_payment_methods(4) := 'DIGITAL_WALLET'; + + FOR i IN 1..v_order_count LOOP + -- Extract values outside SQL statement + v_cust_id := v_cust_ids(MOD(i - 1, v_cust_ids.COUNT) + 1); + v_emp_id := v_emp_ids(MOD(i - 1, v_emp_ids.COUNT) + 1); + v_order_status := v_order_statuses(MOD(i, 5) + 1); + v_payment_method := v_payment_methods(MOD(i, 4) + 1); + + INSERT INTO orders ( + order_id, customer_id, sales_employee_id, order_date, delivery_date, + order_status, total_amount, tax_amount, discount_amount, payment_method + ) VALUES ( + seq_orders.NEXTVAL, + v_cust_id, + v_emp_id, + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 30)), + v_order_status, + ROUND(DBMS_RANDOM.VALUE(50, 5000), 2), + ROUND(DBMS_RANDOM.VALUE(5, 500), 2), + ROUND(DBMS_RANDOM.VALUE(0, 200), 2), + v_payment_method + ); + + IF MOD(i, v_batch_size) = 0 THEN + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ... ' || i || ' orders created'); + END IF; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Orders created: 100000'); +END; +/ + +-- ============================================================================ +-- PHASE 8: POPULATE ORDER ITEMS (avg 7 items per order = 700000 items) +-- ============================================================================ + +DECLARE + TYPE num_array IS TABLE OF NUMBER; + v_order_ids num_array; + v_product_ids num_array; + v_batch_size NUMBER := 10000; + v_item_count NUMBER := 0; + v_orders_count NUMBER; + v_order_id NUMBER; + v_product_id NUMBER; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 8: Populating Order Items (~700000 records)...'); + + SELECT order_id BULK COLLECT INTO v_order_ids FROM orders ORDER BY DBMS_RANDOM.VALUE; + SELECT product_id BULK COLLECT INTO v_product_ids FROM products WHERE discontinued_flag = 'N'; + + v_orders_count := v_order_ids.COUNT; + + FOR i IN 1..v_orders_count LOOP + -- Create 5-10 items per order + FOR j IN 1..TRUNC(DBMS_RANDOM.VALUE(5, 11)) LOOP + v_item_count := v_item_count + 1; + + -- Extract values outside SQL statement + v_order_id := v_order_ids(i); + v_product_id := v_product_ids(MOD(v_item_count - 1, v_product_ids.COUNT) + 1); + + INSERT INTO order_items ( + order_item_id, order_id, product_id, quantity, unit_price, line_total + ) VALUES ( + seq_order_items.NEXTVAL, + v_order_id, + v_product_id, + TRUNC(DBMS_RANDOM.VALUE(1, 20)), + ROUND(DBMS_RANDOM.VALUE(10, 800), 2), + ROUND(TRUNC(DBMS_RANDOM.VALUE(1, 20)) * DBMS_RANDOM.VALUE(10, 800), 2) + ); + + IF MOD(v_item_count, v_batch_size) = 0 THEN + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ... ' || v_item_count || ' order items created'); + END IF; + END LOOP; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Order Items created: ' || v_item_count); +END; +/ + +-- ============================================================================ +-- PHASE 9: POPULATE CUSTOMER REVIEWS (100000 reviews) +-- ============================================================================ + +DECLARE + TYPE num_array IS TABLE OF NUMBER; + v_cust_ids num_array; + v_product_ids num_array; + v_review_count NUMBER := 100000; + v_batch_size NUMBER := 1000; + v_review_texts VARCHAR2(4000) := 'Excellent product,Very satisfied,Good quality,Poor quality,Amazing,Terrible,Works as expected,Defective,Highly recommend,Do not buy,Worth the price,Overpriced,Great value,Disappointing,Exceptional,Awful,Perfect fit,Wrong size,Exceeded expectations,Not as described'; + v_review_array DBMS_SQL.VARCHAR2_TABLE; + v_cust_id NUMBER; + v_product_id NUMBER; +BEGIN + DBMS_OUTPUT.PUT_LINE('>> PHASE 9: Populating Customer Reviews (100000 records)...'); + + SELECT customer_id BULK COLLECT INTO v_cust_ids FROM customers WHERE ROWNUM <= 8000; + SELECT product_id BULK COLLECT INTO v_product_ids FROM products WHERE ROWNUM <= 4000; + + FOR i IN 1..v_review_count LOOP + -- Extract values outside SQL statement + v_cust_id := v_cust_ids(MOD(i - 1, v_cust_ids.COUNT) + 1); + v_product_id := v_product_ids(MOD(i - 1, v_product_ids.COUNT) + 1); + + INSERT INTO customer_reviews ( + review_id, customer_id, product_id, review_rating, review_text, review_status + ) VALUES ( + i, + v_cust_id, + v_product_id, + TRUNC(DBMS_RANDOM.VALUE(1, 6)), + TRIM(REGEXP_SUBSTR(v_review_texts, '[^,]+', 1, MOD(i, 20) + 1)) || ' - ' || i, + CASE WHEN MOD(i, 10) < 8 THEN 'APPROVED' + WHEN MOD(i, 10) = 8 THEN 'PENDING' + ELSE 'REJECTED' + END + ); + + IF MOD(i, v_batch_size) = 0 THEN + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ... ' || i || ' reviews created'); + END IF; + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE(' ✓ Customer Reviews created: 100000'); +END; +/ + +-- ============================================================================ +-- RE-ENABLE CONSTRAINTS +-- ============================================================================ + +DECLARE +BEGIN + DBMS_OUTPUT.PUT_LINE('>> RE-ENABLING CONSTRAINTS...'); + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE employees ENABLE CONSTRAINT fk_emp_dept'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE employees ENABLE CONSTRAINT fk_emp_mgr'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE products ENABLE CONSTRAINT fk_prod_cat'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE orders ENABLE CONSTRAINT fk_ord_cust'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE orders ENABLE CONSTRAINT fk_ord_emp'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE order_items ENABLE CONSTRAINT fk_oi_ord'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE order_items ENABLE CONSTRAINT fk_oi_prod'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE inventory ENABLE CONSTRAINT fk_inv_prod'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE customer_reviews ENABLE CONSTRAINT fk_rev_cust'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE customer_reviews ENABLE CONSTRAINT fk_rev_prod'; + EXCEPTION WHEN OTHERS THEN NULL; + END; + + DBMS_OUTPUT.PUT_LINE(' ✓ All constraints re-enabled'); +END; +/ + +-- ============================================================================ +-- GATHER STATISTICS +-- ============================================================================ + +BEGIN + DBMS_OUTPUT.PUT_LINE('>> GATHERING TABLE STATISTICS...'); + DBMS_STATS.GATHER_SCHEMA_STATS('', DEGREE => 8); + DBMS_OUTPUT.PUT_LINE(' ✓ Statistics gathered'); +END; +/ + +-- ============================================================================ +-- DATA SUMMARY REPORT +-- ============================================================================ + +SET ECHO ON +SET FEEDBACK ON +SET PAGESIZE 50 +SET LINESIZE 120 + +SPOOL /tmp/data_summary.txt + +PROMPT +PROMPT ================================================================================ +PROMPT E-COMMERCE SCHEMA - DATA POPULATION SUMMARY +PROMPT ================================================================================ +PROMPT + +SELECT 'Departments' AS table_name, COUNT(*) AS row_count FROM departments +UNION ALL +SELECT 'Employees', COUNT(*) FROM employees +UNION ALL +SELECT 'Product Categories', COUNT(*) FROM product_categories +UNION ALL +SELECT 'Products', COUNT(*) FROM products +UNION ALL +SELECT 'Customers', COUNT(*) FROM customers +UNION ALL +SELECT 'Inventory', COUNT(*) FROM inventory +UNION ALL +SELECT 'Orders', COUNT(*) FROM orders +UNION ALL +SELECT 'Order Items', COUNT(*) FROM order_items +UNION ALL +SELECT 'Customer Reviews', COUNT(*) FROM customer_reviews +ORDER BY 2 DESC; + +PROMPT +PROMPT ================================================================================ +PROMPT TABLE SIZES (in MB) +PROMPT ================================================================================ +PROMPT + +SELECT + table_name, + ROUND(num_rows * avg_row_len / 1024 / 1024, 2) AS size_mb +FROM user_tables +WHERE table_name IN ('DEPARTMENTS', 'EMPLOYEES', 'PRODUCT_CATEGORIES', 'PRODUCTS', + 'CUSTOMERS', 'INVENTORY', 'ORDERS', 'ORDER_ITEMS', 'CUSTOMER_REVIEWS') +ORDER BY num_rows DESC; + +PROMPT +PROMPT ================================================================================ +PROMPT DATA QUALITY CHECKS +PROMPT ================================================================================ +PROMPT + +PROMPT Active Customers: +SELECT COUNT(*) FROM customers WHERE account_status = 'ACTIVE'; + +PROMPT Active Employees: +SELECT COUNT(*) FROM employees; + +PROMPT Non-Discontinued Products: +SELECT COUNT(*) FROM products WHERE discontinued_flag = 'N'; + +PROMPT Approved Reviews: +SELECT COUNT(*) FROM customer_reviews WHERE review_status = 'APPROVED'; + +PROMPT Confirmed Orders: +SELECT COUNT(*) FROM orders WHERE order_status IN ('CONFIRMED', 'SHIPPED', 'DELIVERED'); + +PROMPT Average Items per Order: +SELECT ROUND(AVG(item_count), 2) AS avg_items_per_order +FROM ( + SELECT order_id, COUNT(*) AS item_count + FROM order_items + GROUP BY order_id +); + +PROMPT +PROMPT ================================================================================ +PROMPT SCHEMA RELATIONSHIPS VALIDATION +PROMPT ================================================================================ +PROMPT + +PROMPT Orders with Valid Customer Reference: +SELECT COUNT(*) FROM orders o +WHERE EXISTS (SELECT 1 FROM customers c WHERE c.customer_id = o.customer_id); + +PROMPT Order Items with Valid Order Reference: +SELECT COUNT(*) FROM order_items oi +WHERE EXISTS (SELECT 1 FROM orders o WHERE o.order_id = oi.order_id); + +PROMPT Reviews with Valid Product Reference: +SELECT COUNT(*) FROM customer_reviews cr +WHERE EXISTS (SELECT 1 FROM products p WHERE p.product_id = cr.product_id); + +PROMPT Inventory with Valid Product Reference: +SELECT COUNT(*) FROM inventory i +WHERE EXISTS (SELECT 1 FROM products p WHERE p.product_id = i.product_id); + +PROMPT +PROMPT ================================================================================ +PROMPT COMPLETION TIME +PROMPT ================================================================================ +PROMPT + +SPOOL OFF + +-- Clean up helper functions +DROP FUNCTION fn_random_string; +DROP FUNCTION fn_random_email; +DROP FUNCTION fn_random_name; + +PROMPT +PROMPT ✓ DATA POPULATION COMPLETE! +PROMPT +PROMPT Total data size: 5-10 MB +PROMPT Records populated: +PROMPT - Departments: 10 +PROMPT - Employees: 500 +PROMPT - Product Categories: 50 +PROMPT - Products: 5,000 +PROMPT - Customers: 10,000 +PROMPT - Inventory: 5,000 (1:1 with products) +PROMPT - Orders: 100,000 +PROMPT - Order Items: ~700,000 (7-10 items per order) +PROMPT - Customer Reviews: 100,000 +PROMPT ═══════════════════════════════════════════════════════════════════════════════ +PROMPT TOTAL: ~821,560 records +PROMPT +PROMPT Summary report saved to: /tmp/data_summary.txt +PROMPT