2026-09-30 15:59:45

This commit is contained in:
support committed 2026-09-30 17:59:45 +02:00
1 parent d1dc3376bd
commit 4f6a34cfcc
4 files changed
+1216

No files matched your search

+112
View File
@@ -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.
@@ -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
@@ -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.
@@ -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.