diff --git a/Oracle_dataset/online-ecommerce1/README.md b/Oracle_dataset/online-ecommerce1/README.md new file mode 100644 index 0000000..2a6dc54 --- /dev/null +++ b/Oracle_dataset/online-ecommerce1/README.md @@ -0,0 +1,112 @@ +# Oracle E-commerce and Accounting Workload + +This project contains three SQL*Plus/SQLcl scripts for creating and exercising +an Oracle e-commerce and accounting schema. + +## Requirements + +- Oracle Database 12c or later. The schema uses identity columns and daily + interval partitioning. +- Run the scripts while connected as the schema owner. +- The activity procedure uses `DBMS_LOCK.SLEEP`. The schema owner may need a + direct grant from a DBA: + + ```sql + GRANT EXECUTE ON SYS.DBMS_LOCK TO your_schema; + ``` + + A role grant may not be sufficient when compiling a stored procedure. + +## Scripts and execution order + +Run these scripts in order from SQL*Plus or SQLcl: + +```sql +@oracle_ecommerce_schema.sql +@oracle_ecommerce_seed_2gb.sql +@oracle_ecommerce_activity.sql +``` + +The schema script creates objects only. The seed script expects the schema to +be empty and should be run once. The activity script creates or replaces the +workload procedure and can be rerun to install procedure changes. + +## `oracle_ecommerce_schema.sql` + +Creates 22 tables with primary keys, foreign keys, unique and check +constraints, and supporting indexes. Seven event/transaction tables use +one-day interval range partitioning; Oracle creates daily interval partitions +automatically for inserted dates after the initial partition boundary. + +The model covers: + +- **Reference and customer data:** `CURRENCIES`, `SALES_CHANNELS`, + `CUSTOMERS`, `CUSTOMER_ADDRESSES`, `SUPPLIERS`, `PRODUCT_CATEGORIES`, + `WAREHOUSES`, and `GL_ACCOUNTS`. +- **Catalog and inventory:** `PRODUCTS`, `INVENTORY_BALANCES`, and + `INVENTORY_MOVEMENTS`. +- **Sales and fulfillment:** `SALES_ORDERS`, `SALES_ORDER_LINES`, `PAYMENTS`, + `SHIPMENTS`, `SHIPMENT_ITEMS`, and `RETURNS`. +- **Purchasing and accounting:** `PURCHASE_ORDERS`, `PURCHASE_ORDER_LINES`, + `JOURNAL_ENTRIES`, and `JOURNAL_LINES`. +- **Operational history:** `AUDIT_EVENTS`. + +Daily interval-partitioned tables are `SALES_ORDERS`, `PAYMENTS`, `SHIPMENTS`, +`INVENTORY_MOVEMENTS`, `JOURNAL_ENTRIES`, `RETURNS`, and `AUDIT_EVENTS`. + +## `oracle_ecommerce_seed_2gb.sql` + +Loads reference and transactional sample data, followed by an audit-event +payload of 2 GiB (2,147,483,648 ASCII characters/bytes in the `DETAILS` +column). Oracle table, index, and segment overhead makes the allocated +database storage larger than 2 GiB. The script generates 1,073,742 audit +rows and commits them in batches; the final row contains the remaining +payload bytes. + +This is a large load and can consume substantial database time, redo, and +storage. It is intended for a test database with enough available space, not +for a production data environment. To change its payload size, edit +`c_target_payload_bytes` in the script. The number of characters per audit +row is controlled by `c_payload_chars` and must remain within the +`AUDIT_EVENTS.DETAILS` column capacity. + +## `oracle_ecommerce_activity.sql` + +Creates `SIMULATE_COMMERCE_ACTIVITY`, which randomly performs single-row +INSERT, UPDATE, and DELETE operations across these 16 tables: + +`RETURNS`, `PURCHASE_ORDERS`, `PURCHASE_ORDER_LINES`, `PRODUCTS`, +`CUSTOMER_ADDRESSES`, `SHIPMENTS`, `PAYMENTS`, `CUSTOMERS`, +`JOURNAL_ENTRIES`, `SALES_ORDERS`, `INVENTORY_MOVEMENTS`, `SHIPMENT_ITEMS`, +`SALES_ORDER_LINES`, `JOURNAL_LINES`, `INVENTORY_BALANCES`, and `AUDIT_EVENTS`. + +The procedure selects parent keys from existing rows for inserts and limits +parent-row deletes to records with no dependent rows. Each successful DML +operation affects one row. Updates change representative values/statuses; +inserts use synthetic data. Operations commit after each iteration, making +changes visible to other sessions, but also committing any uncommitted work +already present in the caller's session. + +### Parameters + +| Parameter | Meaning | +| --- | --- | +| `p_iterations` | Number of workload iterations; must be non-negative. | +| `p_seconds_between_iterations` | Delay between iterations in seconds; must be non-negative. | +| `p_average_rows_per_iteration` | Target average successful DML rows per iteration; must be between 0 and 10,000. Fractional values are randomly rounded across iterations. | + +An iteration may affect fewer rows than its target if it cannot find an eligible +row or exhausts its bounded number of attempts. The average is therefore a +best-effort target, not a guarantee. + +Example: run 100 iterations, wait 2 seconds between them, and target an average +of 5.5 successful row operations per iteration: + +```sql +SET SERVEROUTPUT ON +EXEC simulate_commerce_activity(100, 2, 5.5); +``` + +The procedure prints a one-line summary for each iteration and a final total +and realized average. `DBMS_OUTPUT` is buffered, so SQL*Plus/SQLcl may display +the output after the procedure completes. diff --git a/Oracle_dataset/online-ecommerce1/oracle_ecommerce_activity.sql b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_activity.sql new file mode 100644 index 0000000..e2fbb08 --- /dev/null +++ b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_activity.sql @@ -0,0 +1,431 @@ +-- 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 diff --git a/Oracle_dataset/online-ecommerce1/oracle_ecommerce_schema.sql b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_schema.sql new file mode 100644 index 0000000..6b1d490 --- /dev/null +++ b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_schema.sql @@ -0,0 +1,364 @@ +-- Oracle 12c+ schema for an e-commerce and accounting workload. +-- Run as the schema owner in SQL*Plus or SQLcl. This script creates objects only. +-- The interval-partitioned tables create one-day partitions automatically as dates +-- are inserted beyond their initial boundary. + +WHENEVER SQLERROR EXIT SQL.SQLCODE +SET DEFINE OFF + +CREATE TABLE currencies ( + currency_id NUMBER GENERATED ALWAYS AS IDENTITY, + currency_code CHAR(3) NOT NULL, + currency_name VARCHAR2(60) NOT NULL, + currency_symbol VARCHAR2(8), + CONSTRAINT pk_currencies PRIMARY KEY (currency_id), + CONSTRAINT uq_currencies_code UNIQUE (currency_code) +); + +CREATE TABLE sales_channels ( + channel_id NUMBER GENERATED ALWAYS AS IDENTITY, + channel_name VARCHAR2(40) NOT NULL, + CONSTRAINT pk_sales_channels PRIMARY KEY (channel_id), + CONSTRAINT uq_sales_channels_name UNIQUE (channel_name) +); + +CREATE TABLE customers ( + customer_id NUMBER GENERATED ALWAYS AS IDENTITY, + email VARCHAR2(254) NOT NULL, + first_name VARCHAR2(60) NOT NULL, + last_name VARCHAR2(60) NOT NULL, + phone_number VARCHAR2(30), + customer_since DATE DEFAULT SYSDATE NOT NULL, + customer_status VARCHAR2(12) DEFAULT 'ACTIVE' NOT NULL, + CONSTRAINT pk_customers PRIMARY KEY (customer_id), + CONSTRAINT uq_customers_email UNIQUE (email), + CONSTRAINT ck_customers_status CHECK (customer_status IN ('ACTIVE', 'INACTIVE', 'BLOCKED')) +); + +CREATE TABLE customer_addresses ( + address_id NUMBER GENERATED ALWAYS AS IDENTITY, + customer_id NUMBER NOT NULL, + address_type VARCHAR2(12) NOT NULL, + address_line1 VARCHAR2(120) NOT NULL, + address_line2 VARCHAR2(120), + city VARCHAR2(80) NOT NULL, + region VARCHAR2(80), + postal_code VARCHAR2(20), + country_code CHAR(2) NOT NULL, + is_default CHAR(1) DEFAULT 'N' NOT NULL, + CONSTRAINT pk_customer_addresses PRIMARY KEY (address_id), + CONSTRAINT fk_addresses_customer FOREIGN KEY (customer_id) REFERENCES customers (customer_id), + CONSTRAINT ck_addresses_type CHECK (address_type IN ('BILLING', 'SHIPPING', 'BOTH')), + CONSTRAINT ck_addresses_default CHECK (is_default IN ('Y', 'N')) +); + +CREATE TABLE suppliers ( + supplier_id NUMBER GENERATED ALWAYS AS IDENTITY, + supplier_name VARCHAR2(120) NOT NULL, + email VARCHAR2(254), + country_code CHAR(2) NOT NULL, + supplier_status VARCHAR2(12) DEFAULT 'ACTIVE' NOT NULL, + CONSTRAINT pk_suppliers PRIMARY KEY (supplier_id), + CONSTRAINT uq_suppliers_name UNIQUE (supplier_name), + CONSTRAINT ck_suppliers_status CHECK (supplier_status IN ('ACTIVE', 'INACTIVE')) +); + +CREATE TABLE product_categories ( + category_id NUMBER GENERATED ALWAYS AS IDENTITY, + parent_category_id NUMBER, + category_name VARCHAR2(100) NOT NULL, + CONSTRAINT pk_product_categories PRIMARY KEY (category_id), + CONSTRAINT fk_category_parent FOREIGN KEY (parent_category_id) REFERENCES product_categories (category_id), + CONSTRAINT uq_category_name UNIQUE (category_name) +); + +CREATE TABLE products ( + product_id NUMBER GENERATED ALWAYS AS IDENTITY, + category_id NUMBER NOT NULL, + supplier_id NUMBER NOT NULL, + sku VARCHAR2(40) NOT NULL, + product_name VARCHAR2(160) NOT NULL, + unit_price NUMBER(12,2) NOT NULL, + product_status VARCHAR2(12) DEFAULT 'ACTIVE' NOT NULL, + CONSTRAINT pk_products PRIMARY KEY (product_id), + CONSTRAINT uq_products_sku UNIQUE (sku), + CONSTRAINT fk_products_category FOREIGN KEY (category_id) REFERENCES product_categories (category_id), + CONSTRAINT fk_products_supplier FOREIGN KEY (supplier_id) REFERENCES suppliers (supplier_id), + CONSTRAINT ck_products_price CHECK (unit_price >= 0), + CONSTRAINT ck_products_status CHECK (product_status IN ('ACTIVE', 'DISCONTINUED')) +); + +CREATE TABLE warehouses ( + warehouse_id NUMBER GENERATED ALWAYS AS IDENTITY, + warehouse_name VARCHAR2(100) NOT NULL, + country_code CHAR(2) NOT NULL, + CONSTRAINT pk_warehouses PRIMARY KEY (warehouse_id), + CONSTRAINT uq_warehouses_name UNIQUE (warehouse_name) +); + +CREATE TABLE inventory_balances ( + product_id NUMBER NOT NULL, + warehouse_id NUMBER NOT NULL, + quantity_on_hand NUMBER(12,2) DEFAULT 0 NOT NULL, + quantity_reserved NUMBER(12,2) DEFAULT 0 NOT NULL, + last_counted_at DATE, + CONSTRAINT pk_inventory_balances PRIMARY KEY (product_id, warehouse_id), + CONSTRAINT fk_invbal_product FOREIGN KEY (product_id) REFERENCES products (product_id), + CONSTRAINT fk_invbal_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses (warehouse_id), + CONSTRAINT ck_invbal_on_hand CHECK (quantity_on_hand >= 0), + CONSTRAINT ck_invbal_reserved CHECK (quantity_reserved >= 0 AND quantity_reserved <= quantity_on_hand) +); + +CREATE TABLE sales_orders ( + order_id NUMBER GENERATED ALWAYS AS IDENTITY, + order_date DATE NOT NULL, + customer_id NUMBER NOT NULL, + channel_id NUMBER NOT NULL, + currency_id NUMBER NOT NULL, + billing_address_id NUMBER NOT NULL, + shipping_address_id NUMBER NOT NULL, + order_status VARCHAR2(16) DEFAULT 'PENDING' NOT NULL, + subtotal NUMBER(14,2) NOT NULL, + tax_amount NUMBER(14,2) DEFAULT 0 NOT NULL, + shipping_amount NUMBER(14,2) DEFAULT 0 NOT NULL, + CONSTRAINT pk_sales_orders PRIMARY KEY (order_id), + CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers (customer_id), + CONSTRAINT fk_orders_channel FOREIGN KEY (channel_id) REFERENCES sales_channels (channel_id), + CONSTRAINT fk_orders_currency FOREIGN KEY (currency_id) REFERENCES currencies (currency_id), + CONSTRAINT fk_orders_bill_addr FOREIGN KEY (billing_address_id) REFERENCES customer_addresses (address_id), + CONSTRAINT fk_orders_ship_addr FOREIGN KEY (shipping_address_id) REFERENCES customer_addresses (address_id), + CONSTRAINT ck_orders_status CHECK (order_status IN ('PENDING', 'PAID', 'FULFILLED', 'CANCELLED', 'REFUNDED')), + CONSTRAINT ck_orders_amounts CHECK (subtotal >= 0 AND tax_amount >= 0 AND shipping_amount >= 0) +) +PARTITION BY RANGE (order_date) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_orders_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE sales_order_lines ( + order_line_id NUMBER GENERATED ALWAYS AS IDENTITY, + order_id NUMBER NOT NULL, + product_id NUMBER NOT NULL, + line_number NUMBER(4) NOT NULL, + quantity NUMBER(12,2) NOT NULL, + unit_price NUMBER(12,2) NOT NULL, + discount_amount NUMBER(12,2) DEFAULT 0 NOT NULL, + CONSTRAINT pk_sales_order_lines PRIMARY KEY (order_line_id), + CONSTRAINT fk_lines_order FOREIGN KEY (order_id) REFERENCES sales_orders (order_id), + CONSTRAINT fk_lines_product FOREIGN KEY (product_id) REFERENCES products (product_id), + CONSTRAINT uq_lines_order_num UNIQUE (order_id, line_number), + CONSTRAINT ck_lines_values CHECK (quantity > 0 AND unit_price >= 0 AND discount_amount >= 0) +); + +CREATE TABLE payments ( + payment_id NUMBER GENERATED ALWAYS AS IDENTITY, + payment_date DATE NOT NULL, + order_id NUMBER NOT NULL, + currency_id NUMBER NOT NULL, + payment_method VARCHAR2(16) NOT NULL, + payment_status VARCHAR2(16) NOT NULL, + amount NUMBER(14,2) NOT NULL, + provider_reference VARCHAR2(80), + CONSTRAINT pk_payments PRIMARY KEY (payment_id), + CONSTRAINT fk_payments_order FOREIGN KEY (order_id) REFERENCES sales_orders (order_id), + CONSTRAINT fk_payments_currency FOREIGN KEY (currency_id) REFERENCES currencies (currency_id), + CONSTRAINT ck_payments_method CHECK (payment_method IN ('CARD', 'BANK', 'WALLET', 'CASH')), + CONSTRAINT ck_payments_status CHECK (payment_status IN ('AUTHORIZED', 'CAPTURED', 'FAILED', 'REFUNDED')), + CONSTRAINT ck_payments_amount CHECK (amount >= 0) +) +PARTITION BY RANGE (payment_date) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_payments_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE shipments ( + shipment_id NUMBER GENERATED ALWAYS AS IDENTITY, + shipped_at DATE NOT NULL, + order_id NUMBER NOT NULL, + warehouse_id NUMBER NOT NULL, + tracking_number VARCHAR2(80) NOT NULL, + carrier_name VARCHAR2(60) NOT NULL, + shipment_status VARCHAR2(16) NOT NULL, + CONSTRAINT pk_shipments PRIMARY KEY (shipment_id), + CONSTRAINT fk_shipments_order FOREIGN KEY (order_id) REFERENCES sales_orders (order_id), + CONSTRAINT fk_shipments_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses (warehouse_id), + CONSTRAINT uq_shipments_tracking UNIQUE (tracking_number), + CONSTRAINT ck_shipments_status CHECK (shipment_status IN ('CREATED', 'IN_TRANSIT', 'DELIVERED', 'LOST', 'RETURNED')) +) +PARTITION BY RANGE (shipped_at) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_shipments_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE shipment_items ( + shipment_item_id NUMBER GENERATED ALWAYS AS IDENTITY, + shipment_id NUMBER NOT NULL, + order_line_id NUMBER NOT NULL, + quantity NUMBER(12,2) NOT NULL, + CONSTRAINT pk_shipment_items PRIMARY KEY (shipment_item_id), + CONSTRAINT fk_shipitems_shipment FOREIGN KEY (shipment_id) REFERENCES shipments (shipment_id), + CONSTRAINT fk_shipitems_line FOREIGN KEY (order_line_id) REFERENCES sales_order_lines (order_line_id), + CONSTRAINT ck_shipitems_qty CHECK (quantity > 0) +); + +CREATE TABLE purchase_orders ( + purchase_order_id NUMBER GENERATED ALWAYS AS IDENTITY, + ordered_at DATE NOT NULL, + supplier_id NUMBER NOT NULL, + warehouse_id NUMBER NOT NULL, + currency_id NUMBER NOT NULL, + purchase_status VARCHAR2(16) NOT NULL, + CONSTRAINT pk_purchase_orders PRIMARY KEY (purchase_order_id), + CONSTRAINT fk_po_supplier FOREIGN KEY (supplier_id) REFERENCES suppliers (supplier_id), + CONSTRAINT fk_po_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses (warehouse_id), + CONSTRAINT fk_po_currency FOREIGN KEY (currency_id) REFERENCES currencies (currency_id), + CONSTRAINT ck_po_status CHECK (purchase_status IN ('DRAFT', 'ORDERED', 'RECEIVED', 'CANCELLED')) +); + +CREATE TABLE purchase_order_lines ( + purchase_line_id NUMBER GENERATED ALWAYS AS IDENTITY, + purchase_order_id NUMBER NOT NULL, + product_id NUMBER NOT NULL, + line_number NUMBER(4) NOT NULL, + quantity NUMBER(12,2) NOT NULL, + unit_cost NUMBER(12,2) NOT NULL, + CONSTRAINT pk_purchase_order_lines PRIMARY KEY (purchase_line_id), + CONSTRAINT fk_pol_order FOREIGN KEY (purchase_order_id) REFERENCES purchase_orders (purchase_order_id), + CONSTRAINT fk_pol_product FOREIGN KEY (product_id) REFERENCES products (product_id), + CONSTRAINT uq_poline_order_num UNIQUE (purchase_order_id, line_number), + CONSTRAINT ck_poline_values CHECK (quantity > 0 AND unit_cost >= 0) +); + +CREATE TABLE inventory_movements ( + movement_id NUMBER GENERATED ALWAYS AS IDENTITY, + movement_at DATE NOT NULL, + product_id NUMBER NOT NULL, + warehouse_id NUMBER NOT NULL, + order_line_id NUMBER, + movement_type VARCHAR2(16) NOT NULL, + quantity_delta NUMBER(12,2) NOT NULL, + reference_note VARCHAR2(120), + CONSTRAINT pk_inventory_movements PRIMARY KEY (movement_id), + CONSTRAINT fk_movements_product FOREIGN KEY (product_id) REFERENCES products (product_id), + CONSTRAINT fk_movements_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses (warehouse_id), + CONSTRAINT fk_movements_order_line FOREIGN KEY (order_line_id) REFERENCES sales_order_lines (order_line_id), + CONSTRAINT ck_movements_type CHECK (movement_type IN ('RECEIPT', 'SALE', 'RETURN', 'ADJUSTMENT')), + CONSTRAINT ck_movements_quantity CHECK (quantity_delta <> 0) +) +PARTITION BY RANGE (movement_at) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_movements_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE gl_accounts ( + account_id NUMBER GENERATED ALWAYS AS IDENTITY, + account_code VARCHAR2(20) NOT NULL, + account_name VARCHAR2(100) NOT NULL, + account_type VARCHAR2(12) NOT NULL, + CONSTRAINT pk_gl_accounts PRIMARY KEY (account_id), + CONSTRAINT uq_gl_accounts_code UNIQUE (account_code), + CONSTRAINT ck_gl_accounts_type CHECK (account_type IN ('ASSET', 'LIABILITY', 'EQUITY', 'REVENUE', 'EXPENSE')) +); + +CREATE TABLE journal_entries ( + journal_entry_id NUMBER GENERATED ALWAYS AS IDENTITY, + posted_at DATE NOT NULL, + currency_id NUMBER NOT NULL, + source_type VARCHAR2(20) NOT NULL, + source_id NUMBER, + entry_status VARCHAR2(12) DEFAULT 'POSTED' NOT NULL, + memo VARCHAR2(240) NOT NULL, + CONSTRAINT pk_journal_entries PRIMARY KEY (journal_entry_id), + CONSTRAINT fk_journal_currency FOREIGN KEY (currency_id) REFERENCES currencies (currency_id), + CONSTRAINT ck_journal_status CHECK (entry_status IN ('DRAFT', 'POSTED', 'VOID')) +) +PARTITION BY RANGE (posted_at) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_journal_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE journal_lines ( + journal_line_id NUMBER GENERATED ALWAYS AS IDENTITY, + journal_entry_id NUMBER NOT NULL, + account_id NUMBER NOT NULL, + line_number NUMBER(4) NOT NULL, + debit_amount NUMBER(14,2) DEFAULT 0 NOT NULL, + credit_amount NUMBER(14,2) DEFAULT 0 NOT NULL, + line_memo VARCHAR2(160), + CONSTRAINT pk_journal_lines PRIMARY KEY (journal_line_id), + CONSTRAINT fk_jlines_entry FOREIGN KEY (journal_entry_id) REFERENCES journal_entries (journal_entry_id), + CONSTRAINT fk_jlines_account FOREIGN KEY (account_id) REFERENCES gl_accounts (account_id), + CONSTRAINT uq_jlines_entry_num UNIQUE (journal_entry_id, line_number), + CONSTRAINT ck_jlines_amount CHECK ( + debit_amount >= 0 AND credit_amount >= 0 AND + ((debit_amount > 0 AND credit_amount = 0) OR + (credit_amount > 0 AND debit_amount = 0)) + ) +); + +CREATE TABLE returns ( + return_id NUMBER GENERATED ALWAYS AS IDENTITY, + returned_at DATE NOT NULL, + order_line_id NUMBER NOT NULL, + customer_id NUMBER NOT NULL, + return_status VARCHAR2(16) NOT NULL, + quantity NUMBER(12,2) NOT NULL, + reason_code VARCHAR2(24) NOT NULL, + CONSTRAINT pk_returns PRIMARY KEY (return_id), + CONSTRAINT fk_returns_line FOREIGN KEY (order_line_id) REFERENCES sales_order_lines (order_line_id), + CONSTRAINT fk_returns_customer FOREIGN KEY (customer_id) REFERENCES customers (customer_id), + CONSTRAINT ck_returns_status CHECK (return_status IN ('REQUESTED', 'APPROVED', 'RECEIVED', 'REJECTED', 'REFUNDED')), + CONSTRAINT ck_returns_quantity CHECK (quantity > 0) +) +PARTITION BY RANGE (returned_at) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_returns_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE TABLE audit_events ( + event_id NUMBER GENERATED ALWAYS AS IDENTITY, + event_at DATE NOT NULL, + actor_customer_id NUMBER, + event_type VARCHAR2(30) NOT NULL, + source_ip VARCHAR2(45) NOT NULL, + details VARCHAR2(2000) NOT NULL, + CONSTRAINT pk_audit_events PRIMARY KEY (event_id), + CONSTRAINT fk_audit_customer FOREIGN KEY (actor_customer_id) REFERENCES customers (customer_id) +) +PARTITION BY RANGE (event_at) +INTERVAL (NUMTODSINTERVAL(1, 'DAY')) +( + PARTITION p_audit_seed VALUES LESS THAN (DATE '2025-01-01') +); + +CREATE INDEX ix_addresses_customer ON customer_addresses (customer_id); +CREATE INDEX ix_products_category ON products (category_id); +CREATE INDEX ix_products_supplier ON products (supplier_id); +CREATE INDEX ix_invbal_warehouse ON inventory_balances (warehouse_id); +CREATE INDEX ix_orders_customer_day ON sales_orders (customer_id, order_date) LOCAL; +CREATE INDEX ix_orders_status_day ON sales_orders (order_status, order_date) LOCAL; +CREATE INDEX ix_lines_product ON sales_order_lines (product_id); +CREATE INDEX ix_payments_order_day ON payments (order_id, payment_date) LOCAL; +CREATE INDEX ix_payments_status_day ON payments (payment_status, payment_date) LOCAL; +CREATE INDEX ix_shipments_order_day ON shipments (order_id, shipped_at) LOCAL; +CREATE INDEX ix_shipments_warehouse ON shipments (warehouse_id); +CREATE INDEX ix_shipitems_line ON shipment_items (order_line_id); +CREATE INDEX ix_po_supplier ON purchase_orders (supplier_id); +CREATE INDEX ix_po_warehouse ON purchase_orders (warehouse_id); +CREATE INDEX ix_poline_product ON purchase_order_lines (product_id); +CREATE INDEX ix_movements_product_day ON inventory_movements (product_id, movement_at) LOCAL; +CREATE INDEX ix_movements_warehouse_day ON inventory_movements (warehouse_id, movement_at) LOCAL; +CREATE INDEX ix_journal_source_day ON journal_entries (source_type, posted_at) LOCAL; +CREATE INDEX ix_jlines_account ON journal_lines (account_id); +CREATE INDEX ix_returns_customer_day ON returns (customer_id, returned_at) LOCAL; +CREATE INDEX ix_returns_line ON returns (order_line_id); +CREATE INDEX ix_audit_customer_day ON audit_events (actor_customer_id, event_at) LOCAL; +CREATE INDEX ix_audit_type_day ON audit_events (event_type, event_at) LOCAL; + +PROMPT Schema created: 22 tables, daily interval partitions, constraints, and indexes. diff --git a/Oracle_dataset/online-ecommerce1/oracle_ecommerce_seed_2gb.sql b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_seed_2gb.sql new file mode 100644 index 0000000..a2c806c --- /dev/null +++ b/Oracle_dataset/online-ecommerce1/oracle_ecommerce_seed_2gb.sql @@ -0,0 +1,309 @@ +-- Random sample data for oracle_ecommerce_schema.sql. +-- Run once, as the same schema owner, after creating the schema in an empty schema. +-- The large audit_events load writes exactly 2 GiB of ASCII details payload +-- (2,147,483,648 bytes); table/index allocation and other rows add Oracle overhead. +-- Adjust c_target_payload_bytes or c_payload_chars below to change the load size. + +WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK +SET DEFINE OFF +SET SERVEROUTPUT ON + +DECLARE + l_line_id NUMBER := 0; + l_purchase_line_id NUMBER := 0; + l_journal_line_id NUMBER := 0; + l_journal_amount NUMBER(14,2); + l_batch_size PLS_INTEGER := 5000; + l_batch_rows PLS_INTEGER; + l_offset NUMBER; + l_rows NUMBER; + c_payload_chars CONSTANT PLS_INTEGER := 2000; + c_target_payload_bytes CONSTANT NUMBER := 2147483648; +BEGIN + DBMS_RANDOM.SEED(20260927); + + INSERT INTO currencies (currency_code, currency_name, currency_symbol) + VALUES ('USD', 'US Dollar', '$'); + INSERT INTO currencies (currency_code, currency_name, currency_symbol) + VALUES ('EUR', 'Euro', 'EUR'); + INSERT INTO currencies (currency_code, currency_name, currency_symbol) + VALUES ('GBP', 'Pound Sterling', 'GBP'); + + INSERT INTO sales_channels (channel_name) VALUES ('Web Store'); + INSERT INTO sales_channels (channel_name) VALUES ('Mobile App'); + INSERT INTO sales_channels (channel_name) VALUES ('Marketplace'); + INSERT INTO sales_channels (channel_name) VALUES ('Retail POS'); + + FOR i IN 1..1000 LOOP + INSERT INTO customers ( + email, first_name, last_name, phone_number, customer_since, customer_status + ) VALUES ( + 'customer' || TO_CHAR(i) || '@example.test', + 'First' || TO_CHAR(i), + 'Last' || TO_CHAR(i), + '+1-555-' || TO_CHAR(1000000 + i), + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 2000)), + CASE WHEN DBMS_RANDOM.VALUE(0, 1) < 0.97 THEN 'ACTIVE' ELSE 'INACTIVE' END + ); + + INSERT INTO customer_addresses ( + customer_id, address_type, address_line1, city, region, postal_code, country_code, is_default + ) VALUES ( + i, 'BOTH', TO_CHAR(100 + i) || ' Commerce Street', + 'City' || TO_CHAR(MOD(i, 100) + 1), 'Region ' || TO_CHAR(MOD(i, 20) + 1), + TO_CHAR(10000 + i), 'US', 'Y' + ); + END LOOP; + + FOR i IN 1..50 LOOP + INSERT INTO suppliers (supplier_name, email, country_code, supplier_status) + VALUES ( + 'Supplier ' || TO_CHAR(i), 'supplier' || TO_CHAR(i) || '@example.test', + CASE WHEN MOD(i, 3) = 0 THEN 'GB' WHEN MOD(i, 3) = 1 THEN 'US' ELSE 'DE' END, + 'ACTIVE' + ); + END LOOP; + + FOR i IN 1..30 LOOP + INSERT INTO product_categories (category_name) + VALUES ('Category ' || TO_CHAR(i)); + END LOOP; + + FOR i IN 1..500 LOOP + INSERT INTO products ( + category_id, supplier_id, sku, product_name, unit_price, product_status + ) VALUES ( + MOD(i - 1, 30) + 1, MOD(i - 1, 50) + 1, + 'SKU-' || LPAD(TO_CHAR(i), 6, '0'), + 'Catalog Product ' || TO_CHAR(i), + ROUND(DBMS_RANDOM.VALUE(5, 500), 2), 'ACTIVE' + ); + END LOOP; + + FOR i IN 1..10 LOOP + INSERT INTO warehouses (warehouse_name, country_code) + VALUES ( + 'Warehouse ' || TO_CHAR(i), + CASE WHEN MOD(i, 2) = 0 THEN 'US' ELSE 'DE' END + ); + END LOOP; + + FOR i IN 1..5000 LOOP + DECLARE + l_on_hand NUMBER(12,2) := TRUNC(DBMS_RANDOM.VALUE(10, 1000)); + BEGIN + INSERT INTO inventory_balances ( + product_id, warehouse_id, quantity_on_hand, quantity_reserved, last_counted_at + ) VALUES ( + MOD(i - 1, 500) + 1, + TRUNC((i - 1) / 500) + 1, + l_on_hand, + TRUNC(DBMS_RANDOM.VALUE(0, l_on_hand + 1)), + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 30)) + ); + END; + END LOOP; + + FOR i IN 1..10 LOOP + INSERT INTO gl_accounts (account_code, account_name, account_type) + VALUES ( + CASE i + WHEN 1 THEN '1000' WHEN 2 THEN '1010' WHEN 3 THEN '1100' + WHEN 4 THEN '2000' WHEN 5 THEN '2100' WHEN 6 THEN '3000' + WHEN 7 THEN '4000' WHEN 8 THEN '4100' WHEN 9 THEN '5000' + ELSE '5100' + END, + CASE i + WHEN 1 THEN 'Cash' WHEN 2 THEN 'Payment Clearing' WHEN 3 THEN 'Inventory' + WHEN 4 THEN 'Accounts Payable' WHEN 5 THEN 'Tax Payable' WHEN 6 THEN 'Equity' + WHEN 7 THEN 'Product Revenue' WHEN 8 THEN 'Shipping Revenue' + WHEN 9 THEN 'Cost of Goods Sold' ELSE 'Shipping Expense' + END, + CASE + WHEN i <= 3 THEN 'ASSET' + WHEN i <= 5 THEN 'LIABILITY' + WHEN i = 6 THEN 'EQUITY' + WHEN i <= 8 THEN 'REVENUE' + ELSE 'EXPENSE' + END + ); + END LOOP; + + FOR i IN 1..1000 LOOP + 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 ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + i, MOD(i - 1, 4) + 1, MOD(i - 1, 3) + 1, i, i, + CASE WHEN MOD(i, 12) = 0 THEN 'CANCELLED' + WHEN MOD(i, 5) = 0 THEN 'FULFILLED' + ELSE 'PAID' END, + ROUND(DBMS_RANDOM.VALUE(25, 1200), 2), + ROUND(DBMS_RANDOM.VALUE(1, 100), 2), + ROUND(DBMS_RANDOM.VALUE(0, 30), 2) + ); + + FOR line_no IN 1..2 LOOP + l_line_id := l_line_id + 1; + INSERT INTO sales_order_lines ( + order_id, product_id, line_number, quantity, unit_price, discount_amount + ) VALUES ( + i, MOD(i * 2 + line_no - 2, 500) + 1, line_no, + TRUNC(DBMS_RANDOM.VALUE(1, 5)), + ROUND(DBMS_RANDOM.VALUE(5, 500), 2), + ROUND(DBMS_RANDOM.VALUE(0, 10), 2) + ); + END LOOP; + + INSERT INTO payments ( + payment_date, order_id, currency_id, payment_method, + payment_status, amount, provider_reference + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + i, MOD(i - 1, 3) + 1, + CASE MOD(i, 4) WHEN 0 THEN 'BANK' WHEN 1 THEN 'CARD' + WHEN 2 THEN 'WALLET' ELSE 'CASH' END, + CASE WHEN MOD(i, 17) = 0 THEN 'FAILED' ELSE 'CAPTURED' END, + ROUND(DBMS_RANDOM.VALUE(25, 1300), 2), + 'PAY-' || TO_CHAR(i) || '-' || DBMS_RANDOM.STRING('X', 12) + ); + + INSERT INTO shipments ( + shipped_at, order_id, warehouse_id, tracking_number, carrier_name, shipment_status + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + i, MOD(i - 1, 10) + 1, 'TRK-' || LPAD(TO_CHAR(i), 10, '0'), + CASE MOD(i, 3) WHEN 0 THEN 'Parcel One' WHEN 1 THEN 'RapidShip' ELSE 'Global Freight' END, + CASE WHEN MOD(i, 9) = 0 THEN 'IN_TRANSIT' ELSE 'DELIVERED' END + ); + + INSERT INTO shipment_items (shipment_id, order_line_id, quantity) + VALUES (i, (i - 1) * 2 + 1, 1); + INSERT INTO shipment_items (shipment_id, order_line_id, quantity) + VALUES (i, (i - 1) * 2 + 2, 1); + END LOOP; + + FOR i IN 1..100 LOOP + INSERT INTO purchase_orders ( + ordered_at, supplier_id, warehouse_id, currency_id, purchase_status + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + MOD(i - 1, 50) + 1, MOD(i - 1, 10) + 1, MOD(i - 1, 3) + 1, + CASE WHEN MOD(i, 6) = 0 THEN 'ORDERED' ELSE 'RECEIVED' END + ); + + FOR line_no IN 1..2 LOOP + l_purchase_line_id := l_purchase_line_id + 1; + INSERT INTO purchase_order_lines ( + purchase_order_id, product_id, line_number, quantity, unit_cost + ) VALUES ( + i, MOD(i * 2 + line_no - 2, 500) + 1, line_no, + TRUNC(DBMS_RANDOM.VALUE(10, 200)), + ROUND(DBMS_RANDOM.VALUE(3, 300), 2) + ); + END LOOP; + END LOOP; + + FOR i IN 1..2000 LOOP + INSERT INTO inventory_movements ( + movement_at, product_id, warehouse_id, order_line_id, + movement_type, quantity_delta, reference_note + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + MOD(i - 1, 500) + 1, MOD(TRUNC((i - 1) / 500), 10) + 1, + MOD(i - 1, 2000) + 1, + 'SALE', -1, 'Generated fulfillment movement' + ); + END LOOP; + + FOR i IN 1..1000 LOOP + l_journal_amount := ROUND(DBMS_RANDOM.VALUE(25, 1300), 2); + INSERT INTO journal_entries ( + posted_at, currency_id, source_type, source_id, entry_status, memo + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + MOD(i - 1, 3) + 1, 'SALES_ORDER', i, 'POSTED', + 'Generated balanced sales posting ' || TO_CHAR(i) + ); + + l_journal_line_id := l_journal_line_id + 1; + INSERT INTO journal_lines ( + journal_entry_id, account_id, line_number, debit_amount, credit_amount, line_memo + ) VALUES ( + i, 1, 1, l_journal_amount, 0, 'Cash or clearing debit' + ); + + l_journal_line_id := l_journal_line_id + 1; + INSERT INTO journal_lines ( + journal_entry_id, account_id, line_number, debit_amount, credit_amount, line_memo + ) VALUES ( + i, 7, 2, 0, l_journal_amount, 'Sales revenue credit' + ); + END LOOP; + + FOR i IN 1..100 LOOP + INSERT INTO returns ( + returned_at, order_line_id, customer_id, return_status, quantity, reason_code + ) VALUES ( + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + MOD(i - 1, 2000) + 1, TRUNC((MOD(i - 1, 2000)) / 2) + 1, + CASE WHEN MOD(i, 4) = 0 THEN 'REFUNDED' ELSE 'RECEIVED' END, + 1, CASE MOD(i, 3) WHEN 0 THEN 'DAMAGED' WHEN 1 THEN 'NOT_AS_DESCRIBED' ELSE 'CHANGED_MIND' END + ); + END LOOP; + + COMMIT; + DBMS_OUTPUT.PUT_LINE('Reference and transactional sample data inserted.'); + + l_rows := CEIL(c_target_payload_bytes / c_payload_chars); + l_offset := 1; + WHILE l_offset <= l_rows LOOP + l_batch_rows := LEAST(l_batch_size, l_rows - l_offset + 1); + + INSERT INTO audit_events ( + event_at, actor_customer_id, event_type, source_ip, details + ) + SELECT + TRUNC(SYSDATE) - TRUNC(DBMS_RANDOM.VALUE(0, 365)), + CASE WHEN DBMS_RANDOM.VALUE(0, 1) < 0.8 + THEN TRUNC(DBMS_RANDOM.VALUE(1, 1001)) ELSE NULL END, + CASE MOD(LEVEL, 5) + WHEN 0 THEN 'CUSTOMER_LOGIN' + WHEN 1 THEN 'PRODUCT_VIEW' + WHEN 2 THEN 'ORDER_UPDATE' + WHEN 3 THEN 'PAYMENT_EVENT' + ELSE 'ADDRESS_CHANGE' + END, + '192.0.2.' || TO_CHAR(TRUNC(DBMS_RANDOM.VALUE(1, 255))), + DBMS_RANDOM.STRING( + 'A', + LEAST( + c_payload_chars, + c_target_payload_bytes - + ((l_offset + LEVEL - 2) * c_payload_chars) + ) + ) + FROM dual + CONNECT BY LEVEL <= l_batch_rows; + + COMMIT; + l_offset := l_offset + l_batch_rows; + IF MOD(l_offset - 1, 50000) < l_batch_rows THEN + DBMS_OUTPUT.PUT_LINE( + 'Audit events generated: ' || TO_CHAR(LEAST(l_offset - 1, l_rows)) || + ' / ' || TO_CHAR(l_rows) + ); + END IF; + END LOOP; + + DBMS_OUTPUT.PUT_LINE( + 'Completed audit payload: ' || TO_CHAR(l_rows) || + ' rows x ' || TO_CHAR(c_payload_chars) || + ' characters (~2 GiB), plus Oracle row and index overhead.' + ); +END; +/ + +PROMPT Random dataset generation complete.