資料庫開發與效能測試常需大量測試資料。逐筆 INSERT 不僅緩慢,更難模擬真實分布。Oracle 內建 DBMS_RANDOM 套件結合 CONNECT BY LEVEL,能以單一 SQL 秒級生成百萬筆具備隨機特性的測試資料。本文系統整理常用隨機生成模式,並附上可直接套用的範本。
select rownum as id,
to_char(sysdate + rownum / 24 / 3600, 'yyyy-mm-dd hh24:mi:ss') as inc_datetime,
trunc(dbms_random.value(0, 100)) as random_id,
dbms_random.string('$', 20) random_string
from dual connect by level <= 100;
CONNECT BY LEVEL <= N 產生 N 列虛擬資料列,配合 ROWNUM 即為流水號,再呼叫 DBMS_RANDOM 各函數填充欄位。
| 需求 | SQL 範例 | 說明 |
|---|---|---|
| 0/1 隨機(如性別) | decode(trunc(dbms_random.value(0,2)), 0, '女', 1, '男') | 利用 DECODE 映射 |
| 9~11 位隨機數字 | length(dbms_random.random) | RANDOM 回傳整數,長度不固定 |
| 0~99 隨機整數 | abs(mod(dbms_random.random, 100)) | 取絕對值後取模 |
| 10~99 兩位整數 | trunc(10 + 90 * dbms_random.value) | 含下界、不含上界 |
| 0~1 隨機小數 | dbms_random.value | 37~38 位小數精度 |
| 指定區間小數 | dbms_random.value(10, 20) | 大於 10、小於 20 |
| 常態分布隨機數 | dbms_random.normal | μ=0, σ=1,68% 落在 ±1 |
| 場景 | SQL 範例 |
|---|---|
| 過去 10 天任意日期 | to_date(trunc(dbms_random.value(to_number(to_char(sysdate-10,'J')), to_number(to_char(sysdate,'J')))), 'J') |
| 過去 30 分鐘任意時間 | (sysdate - 1/24/60*30) + dbms_random.value(1,1800)/3600/24 |
| 2015 年任意日期 | to_date(to_char(to_date('2015-01-01','yyyy-MM-dd'),'J') + trunc(DBMS_RANDOM.VALUE(0,365)), 'J') |
| 指定日期區間(含時間) | to_date(trunc(dbms_random.value(to_number(to_char(to_date('20150501','yyyymmdd'),'J')), to_number(to_char(to_date('20150531','yyyymmdd')+1,'J')))),'J') + dbms_random.value(1,3600)/3600 |
| 過去 100 年任意時刻 | to_date(trunc(dbms_random.value(to_number(to_char(add_months(sysdate,-1200),'J')), to_number(to_char(sysdate+1,'J')))),'J') + dbms_random.value(1,3600)/3600 |
關鍵技巧:Oracle 以 Julian Day(J 格式)進行日期運算,轉數字後用 DBMS_RANDOM.VALUE 取亂數,再轉回日期。
| 參數 | 輸出字元集 | 範例 |
|---|---|---|
| 'u' 或 'U' | 大寫字母 | dbms_random.string('u', 20) |
| 'l' 或 'L' | 小寫字母 | dbms_random.string('l', 20) |
| 'a' 或 'A' | 大小寫混合 | dbms_random.string('a', 20) |
| 'x' 或 'X' | 大寫字母+數字 | dbms_random.string('x', 20) |
| 'p' 或 'P' | 可列印字元(含符號) | dbms_random.string('p', 20) |
insert into orders (order_id, cust_id, order_date, amount, status)
select level,
trunc(dbms_random.value(1, 100000)),
to_date(trunc(dbms_random.value(to_number(to_char(sysdate-365,'J')), to_number(to_char(sysdate,'J')))), 'J'),
round(dbms_random.value(10, 10000), 2),
decode(trunc(dbms_random.value(1,5)), 1,'NEW',2,'PAID',3,'SHIPPED',4,'CANCELLED')
from dual connect by level <= 1000000;
單條 SQL 即可完成百萬筆插入,效率遠超 PL/SQL 迴圈或外部程式批次寫入。
APPEND Hint:insert /*+ APPEND */ into ...DBMS_STATS.GATHER_TABLE_STATS 更新統計資訊DBMS_RANDOM.SEED(val) 固定種子善用 DBMS_RANDOM,讓測試資料準備從「小時級」縮短為「秒級」。