2026-07-19 16:14:58

This commit is contained in:
2026-07-19 18:14:58 +02:00
parent 719cd37844
commit c0621c9953
2 changed files with 1452 additions and 0 deletions
+785
View File
@@ -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
-- ============================================================================
+667
View File
@@ -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