-- Creates a workload procedure for the e-commerce schema. -- Run as the schema owner after oracle_ecommerce_schema.sql. The workload -- commits after each iteration so other sessions can observe the activity. -- DBMS_LOCK.SLEEP may require EXECUTE on SYS.DBMS_LOCK for the schema owner. -- The average is realized by randomly rounding its fractional part per iteration; -- each successful insert, update, or delete affects one row. Example: -- EXEC simulate_commerce_activity(100, 2, 5.5); WHENEVER SQLERROR EXIT SQL.SQLCODE SET DEFINE OFF CREATE OR REPLACE PROCEDURE simulate_commerce_activity ( p_iterations IN PLS_INTEGER, p_seconds_between_iterations IN NUMBER, p_average_rows_per_iteration IN NUMBER ) AS TYPE t_varchar_array IS TABLE OF VARCHAR2(1000) INDEX BY PLS_INTEGER; l_tables t_varchar_array; l_update_sets t_varchar_array; l_delete_tests t_varchar_array; l_table_index PLS_INTEGER; l_action PLS_INTEGER; l_target_rows PLS_INTEGER; l_rows_done PLS_INTEGER; l_rows_total NUMBER := 0; l_rows_this PLS_INTEGER; l_attempts PLS_INTEGER; l_rowid UROWID; l_id1 NUMBER; l_id2 NUMBER; l_id3 NUMBER; l_id4 NUMBER; l_id5 NUMBER; l_line_number NUMBER; l_amount NUMBER(14,2); FUNCTION random_number(p_table IN VARCHAR2, p_column IN VARCHAR2) RETURN NUMBER IS l_result NUMBER; BEGIN EXECUTE IMMEDIATE 'SELECT chosen_value FROM (' || ' SELECT ' || p_column || ' AS chosen_value FROM ' || p_table || ' ORDER BY DBMS_RANDOM.VALUE) WHERE ROWNUM = 1' INTO l_result; RETURN l_result; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END random_number; FUNCTION random_rowid(p_table IN VARCHAR2, p_filter IN VARCHAR2) RETURN UROWID IS l_result UROWID; l_sql VARCHAR2(2000); BEGIN l_sql := 'SELECT chosen_rowid FROM (' || ' SELECT ROWID AS chosen_rowid FROM ' || p_table || ' WHERE ' || NVL(p_filter, '1 = 1') || ' ORDER BY DBMS_RANDOM.VALUE) WHERE ROWNUM = 1'; EXECUTE IMMEDIATE l_sql INTO l_result; RETURN l_result; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END random_rowid; PROCEDURE do_one_activity ( p_table_index IN PLS_INTEGER, p_action IN PLS_INTEGER, p_row_count OUT PLS_INTEGER ) IS BEGIN p_row_count := 0; l_id1 := NULL; l_id2 := NULL; l_id3 := NULL; l_id4 := NULL; l_id5 := NULL; IF p_action = 1 THEN CASE p_table_index WHEN 1 THEN l_id1 := random_number('sales_order_lines', 'order_line_id'); l_id2 := random_number('customers', 'customer_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO returns ( returned_at, order_line_id, customer_id, return_status, quantity, reason_code ) VALUES ( SYSDATE, l_id1, l_id2, 'REQUESTED', 1, 'SIMULATED' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 2 THEN l_id1 := random_number('suppliers', 'supplier_id'); l_id2 := random_number('warehouses', 'warehouse_id'); l_id3 := random_number('currencies', 'currency_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL AND l_id3 IS NOT NULL THEN INSERT INTO purchase_orders ( ordered_at, supplier_id, warehouse_id, currency_id, purchase_status ) VALUES ( SYSDATE, l_id1, l_id2, l_id3, 'DRAFT' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 3 THEN l_id1 := random_number('purchase_orders', 'purchase_order_id'); l_id2 := random_number('products', 'product_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN SELECT NVL(MAX(line_number), 0) + 1 INTO l_line_number FROM purchase_order_lines WHERE purchase_order_id = l_id1; IF l_line_number <= 9999 THEN INSERT INTO purchase_order_lines ( purchase_order_id, product_id, line_number, quantity, unit_cost ) VALUES ( l_id1, l_id2, l_line_number, 1, ROUND(DBMS_RANDOM.VALUE(1, 300), 2) ); p_row_count := SQL%ROWCOUNT; END IF; END IF; WHEN 4 THEN l_id1 := random_number('product_categories', 'category_id'); l_id2 := random_number('suppliers', 'supplier_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO products ( category_id, supplier_id, sku, product_name, unit_price, product_status ) VALUES ( l_id1, l_id2, 'SIM-' || RAWTOHEX(SYS_GUID()), 'Simulated product', ROUND(DBMS_RANDOM.VALUE(1, 500), 2), 'ACTIVE' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 5 THEN l_id1 := random_number('customers', 'customer_id'); IF l_id1 IS NOT NULL THEN INSERT INTO customer_addresses ( customer_id, address_type, address_line1, city, country_code, is_default ) VALUES ( l_id1, 'BOTH', '1 Simulation Way', 'Simulated City', 'US', 'N' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 6 THEN l_id1 := random_number('sales_orders', 'order_id'); l_id2 := random_number('warehouses', 'warehouse_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO shipments ( shipped_at, order_id, warehouse_id, tracking_number, carrier_name, shipment_status ) VALUES ( SYSDATE, l_id1, l_id2, 'SIM-' || RAWTOHEX(SYS_GUID()), 'Simulation Carrier', 'CREATED' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 7 THEN l_id1 := random_number('sales_orders', 'order_id'); l_id2 := random_number('currencies', 'currency_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO payments ( payment_date, order_id, currency_id, payment_method, payment_status, amount, provider_reference ) VALUES ( SYSDATE, l_id1, l_id2, 'CARD', 'AUTHORIZED', ROUND(DBMS_RANDOM.VALUE(1, 1000), 2), 'SIM-' || RAWTOHEX(SYS_GUID()) ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 8 THEN INSERT INTO customers ( email, first_name, last_name, customer_since, customer_status ) VALUES ( 'sim-' || RAWTOHEX(SYS_GUID()) || '@example.test', 'Simulated', 'Customer', SYSDATE, 'ACTIVE' ); p_row_count := SQL%ROWCOUNT; WHEN 9 THEN l_id1 := random_number('currencies', 'currency_id'); IF l_id1 IS NOT NULL THEN INSERT INTO journal_entries ( posted_at, currency_id, source_type, source_id, entry_status, memo ) VALUES ( SYSDATE, l_id1, 'SIMULATION', NULL, 'DRAFT', 'Simulated journal entry' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 10 THEN l_id1 := random_number('customers', 'customer_id'); l_id2 := random_number('sales_channels', 'channel_id'); l_id3 := random_number('currencies', 'currency_id'); l_id4 := random_number('customer_addresses', 'address_id'); l_id5 := random_number('customer_addresses', 'address_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL AND l_id3 IS NOT NULL AND l_id4 IS NOT NULL AND l_id5 IS NOT NULL THEN INSERT INTO sales_orders ( order_date, customer_id, channel_id, currency_id, billing_address_id, shipping_address_id, order_status, subtotal, tax_amount, shipping_amount ) VALUES ( SYSDATE, l_id1, l_id2, l_id3, l_id4, l_id5, 'PENDING', 0, 0, 0 ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 11 THEN l_id1 := random_number('products', 'product_id'); l_id2 := random_number('warehouses', 'warehouse_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO inventory_movements ( movement_at, product_id, warehouse_id, movement_type, quantity_delta, reference_note ) VALUES ( SYSDATE, l_id1, l_id2, 'RECEIPT', 1, 'Simulated stock receipt' ); p_row_count := SQL%ROWCOUNT; END IF; WHEN 12 THEN l_id1 := random_number('shipments', 'shipment_id'); l_id2 := random_number('sales_order_lines', 'order_line_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN INSERT INTO shipment_items (shipment_id, order_line_id, quantity) VALUES (l_id1, l_id2, 1); p_row_count := SQL%ROWCOUNT; END IF; WHEN 13 THEN l_id1 := random_number('sales_orders', 'order_id'); l_id2 := random_number('products', 'product_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN SELECT NVL(MAX(line_number), 0) + 1 INTO l_line_number FROM sales_order_lines WHERE order_id = l_id1; IF l_line_number <= 9999 THEN INSERT INTO sales_order_lines ( order_id, product_id, line_number, quantity, unit_price, discount_amount ) VALUES ( l_id1, l_id2, l_line_number, 1, ROUND(DBMS_RANDOM.VALUE(1, 500), 2), 0 ); p_row_count := SQL%ROWCOUNT; END IF; END IF; WHEN 14 THEN l_id1 := random_number('journal_entries', 'journal_entry_id'); l_id2 := random_number('gl_accounts', 'account_id'); IF l_id1 IS NOT NULL AND l_id2 IS NOT NULL THEN SELECT NVL(MAX(line_number), 0) + 1 INTO l_line_number FROM journal_lines WHERE journal_entry_id = l_id1; IF l_line_number <= 9999 THEN l_amount := ROUND(DBMS_RANDOM.VALUE(1, 1000), 2); INSERT INTO journal_lines ( journal_entry_id, account_id, line_number, debit_amount, credit_amount, line_memo ) VALUES ( l_id1, l_id2, l_line_number, l_amount, 0, 'Simulated debit' ); p_row_count := SQL%ROWCOUNT; END IF; END IF; WHEN 15 THEN BEGIN SELECT product_id, warehouse_id INTO l_id1, l_id2 FROM ( SELECT p.product_id, w.warehouse_id FROM products p CROSS JOIN warehouses w WHERE NOT EXISTS ( SELECT 1 FROM inventory_balances b WHERE b.product_id = p.product_id AND b.warehouse_id = w.warehouse_id ) ORDER BY DBMS_RANDOM.VALUE ) WHERE ROWNUM = 1; INSERT INTO inventory_balances ( product_id, warehouse_id, quantity_on_hand, quantity_reserved, last_counted_at ) VALUES ( l_id1, l_id2, 1, 0, SYSDATE ); p_row_count := SQL%ROWCOUNT; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; WHEN 16 THEN l_id1 := random_number('customers', 'customer_id'); INSERT INTO audit_events ( event_at, actor_customer_id, event_type, source_ip, details ) VALUES ( SYSDATE, l_id1, 'SIMULATED_ACTIVITY', '192.0.2.1', 'Generated by SIMULATE_COMMERCE_ACTIVITY' ); p_row_count := SQL%ROWCOUNT; END CASE; ELSIF p_action = 2 THEN l_rowid := random_rowid(l_tables(p_table_index), NULL); IF l_rowid IS NOT NULL THEN EXECUTE IMMEDIATE 'UPDATE ' || l_tables(p_table_index) || ' SET ' || l_update_sets(p_table_index) || ' WHERE ROWID = :selected_rowid' USING l_rowid; p_row_count := SQL%ROWCOUNT; END IF; ELSE l_rowid := random_rowid( l_tables(p_table_index), l_delete_tests(p_table_index) ); IF l_rowid IS NOT NULL THEN EXECUTE IMMEDIATE 'DELETE FROM ' || l_tables(p_table_index) || ' WHERE ROWID = :selected_rowid' USING l_rowid; p_row_count := SQL%ROWCOUNT; END IF; END IF; END do_one_activity; BEGIN IF p_iterations IS NULL OR p_iterations < 0 THEN RAISE_APPLICATION_ERROR(-20001, 'p_iterations must be a non-negative integer'); END IF; IF p_seconds_between_iterations IS NULL OR p_seconds_between_iterations < 0 THEN RAISE_APPLICATION_ERROR(-20002, 'p_seconds_between_iterations must be non-negative'); END IF; IF p_average_rows_per_iteration IS NULL OR p_average_rows_per_iteration < 0 OR p_average_rows_per_iteration > 10000 THEN RAISE_APPLICATION_ERROR(-20003, 'p_average_rows_per_iteration must be between 0 and 10000'); END IF; l_tables(1) := 'RETURNS'; l_tables(2) := 'PURCHASE_ORDERS'; l_tables(3) := 'PURCHASE_ORDER_LINES'; l_tables(4) := 'PRODUCTS'; l_tables(5) := 'CUSTOMER_ADDRESSES'; l_tables(6) := 'SHIPMENTS'; l_tables(7) := 'PAYMENTS'; l_tables(8) := 'CUSTOMERS'; l_tables(9) := 'JOURNAL_ENTRIES'; l_tables(10) := 'SALES_ORDERS'; l_tables(11) := 'INVENTORY_MOVEMENTS'; l_tables(12) := 'SHIPMENT_ITEMS'; l_tables(13) := 'SALES_ORDER_LINES'; l_tables(14) := 'JOURNAL_LINES'; l_tables(15) := 'INVENTORY_BALANCES'; l_tables(16) := 'AUDIT_EVENTS'; l_update_sets(1) := 'return_status = CASE return_status WHEN ''REQUESTED'' THEN ''APPROVED'' ELSE ''REQUESTED'' END'; l_update_sets(2) := 'purchase_status = CASE purchase_status WHEN ''DRAFT'' THEN ''ORDERED'' WHEN ''ORDERED'' THEN ''RECEIVED'' ELSE ''DRAFT'' END'; l_update_sets(3) := 'quantity = quantity + 1'; l_update_sets(4) := 'unit_price = unit_price + 1, product_status = CASE product_status WHEN ''ACTIVE'' THEN ''DISCONTINUED'' ELSE ''ACTIVE'' END'; l_update_sets(5) := 'city = SUBSTR(city || ''X'', 1, 80)'; l_update_sets(6) := 'shipment_status = CASE shipment_status WHEN ''CREATED'' THEN ''IN_TRANSIT'' WHEN ''IN_TRANSIT'' THEN ''DELIVERED'' ELSE ''CREATED'' END'; l_update_sets(7) := 'payment_status = CASE payment_status WHEN ''AUTHORIZED'' THEN ''CAPTURED'' WHEN ''CAPTURED'' THEN ''REFUNDED'' ELSE ''AUTHORIZED'' END'; l_update_sets(8) := 'phone_number = ''SIM-'' || TO_CHAR(TRUNC(DBMS_RANDOM.VALUE(100000, 999999))), customer_status = CASE customer_status WHEN ''ACTIVE'' THEN ''INACTIVE'' ELSE ''ACTIVE'' END'; l_update_sets(9) := 'entry_status = CASE entry_status WHEN ''DRAFT'' THEN ''POSTED'' WHEN ''POSTED'' THEN ''VOID'' ELSE ''DRAFT'' END, memo = SUBSTR(memo || '' *'', 1, 240)'; l_update_sets(10) := 'order_status = CASE order_status WHEN ''PENDING'' THEN ''PAID'' WHEN ''PAID'' THEN ''FULFILLED'' ELSE ''PENDING'' END, subtotal = subtotal + 1'; l_update_sets(11) := 'quantity_delta = -quantity_delta, reference_note = ''Simulated movement update'''; l_update_sets(12) := 'quantity = quantity + 1'; l_update_sets(13) := 'quantity = quantity + 1'; l_update_sets(14) := 'debit_amount = CASE WHEN debit_amount > 0 THEN debit_amount + 1 ELSE 0 END, credit_amount = CASE WHEN credit_amount > 0 THEN credit_amount + 1 ELSE 0 END'; l_update_sets(15) := 'quantity_on_hand = quantity_on_hand + 1, last_counted_at = SYSDATE'; l_update_sets(16) := 'details = SUBSTR(details || ''x'', 1, 2000)'; l_delete_tests(1) := '1 = 1'; l_delete_tests(2) := 'NOT EXISTS (SELECT 1 FROM purchase_order_lines x WHERE x.purchase_order_id = purchase_orders.purchase_order_id)'; l_delete_tests(3) := '1 = 1'; l_delete_tests(4) := 'NOT EXISTS (SELECT 1 FROM sales_order_lines x WHERE x.product_id = products.product_id) AND NOT EXISTS (SELECT 1 FROM inventory_balances x WHERE x.product_id = products.product_id) AND NOT EXISTS (SELECT 1 FROM inventory_movements x WHERE x.product_id = products.product_id) AND NOT EXISTS (SELECT 1 FROM purchase_order_lines x WHERE x.product_id = products.product_id)'; l_delete_tests(5) := 'NOT EXISTS (SELECT 1 FROM sales_orders x WHERE x.billing_address_id = customer_addresses.address_id OR x.shipping_address_id = customer_addresses.address_id)'; l_delete_tests(6) := 'NOT EXISTS (SELECT 1 FROM shipment_items x WHERE x.shipment_id = shipments.shipment_id)'; l_delete_tests(7) := '1 = 1'; l_delete_tests(8) := 'NOT EXISTS (SELECT 1 FROM customer_addresses x WHERE x.customer_id = customers.customer_id) AND NOT EXISTS (SELECT 1 FROM sales_orders x WHERE x.customer_id = customers.customer_id) AND NOT EXISTS (SELECT 1 FROM returns x WHERE x.customer_id = customers.customer_id) AND NOT EXISTS (SELECT 1 FROM audit_events x WHERE x.actor_customer_id = customers.customer_id)'; l_delete_tests(9) := 'NOT EXISTS (SELECT 1 FROM journal_lines x WHERE x.journal_entry_id = journal_entries.journal_entry_id)'; l_delete_tests(10) := 'NOT EXISTS (SELECT 1 FROM sales_order_lines x WHERE x.order_id = sales_orders.order_id) AND NOT EXISTS (SELECT 1 FROM payments x WHERE x.order_id = sales_orders.order_id) AND NOT EXISTS (SELECT 1 FROM shipments x WHERE x.order_id = sales_orders.order_id)'; l_delete_tests(11) := '1 = 1'; l_delete_tests(12) := '1 = 1'; l_delete_tests(13) := 'NOT EXISTS (SELECT 1 FROM shipment_items x WHERE x.order_line_id = sales_order_lines.order_line_id) AND NOT EXISTS (SELECT 1 FROM inventory_movements x WHERE x.order_line_id = sales_order_lines.order_line_id) AND NOT EXISTS (SELECT 1 FROM returns x WHERE x.order_line_id = sales_order_lines.order_line_id)'; l_delete_tests(14) := '1 = 1'; l_delete_tests(15) := '1 = 1'; l_delete_tests(16) := '1 = 1'; FOR iteration_no IN 1 .. p_iterations LOOP l_target_rows := TRUNC(p_average_rows_per_iteration); IF DBMS_RANDOM.VALUE(0, 1) < p_average_rows_per_iteration - TRUNC(p_average_rows_per_iteration) THEN l_target_rows := l_target_rows + 1; END IF; l_rows_done := 0; l_attempts := 0; WHILE l_rows_done < l_target_rows AND l_attempts < (l_target_rows * 20 + 100) LOOP l_table_index := TRUNC(DBMS_RANDOM.VALUE(1, 17)); l_action := TRUNC(DBMS_RANDOM.VALUE(1, 4)); do_one_activity(l_table_index, l_action, l_rows_this); l_rows_done := l_rows_done + l_rows_this; l_attempts := l_attempts + 1; END LOOP; COMMIT; l_rows_total := l_rows_total + l_rows_done; DBMS_OUTPUT.PUT_LINE( 'Iteration ' || iteration_no || ': ' || l_rows_done || ' row(s) affected (target ' || l_target_rows || ')' ); IF iteration_no < p_iterations AND p_seconds_between_iterations > 0 THEN DBMS_LOCK.SLEEP(p_seconds_between_iterations); END IF; END LOOP; IF p_iterations > 0 THEN DBMS_OUTPUT.PUT_LINE( 'Total rows affected: ' || l_rows_total || '; actual average per iteration: ' || ROUND(l_rows_total / p_iterations, 2) ); END IF; END; / SHOW ERRORS PROCEDURE simulate_commerce_activity