" 1、批量數據操作 " "1.1、批量生成數據" "1.2、批量插入數據" " 2、批量生成腳本 " " 3、生成數據字典 " " 4、常見系統包 " "4.1、DBMS_OUTPUT" "4.2、DBMS_RANDOM" "4.3、其它系統包及常用方法" " 5、總結 " 1、批量數據操作 1. ...
1、批量數據操作
1.1、批量生成數據
生成 1~5 之間的整數
SELECT ROWNUM,LEVEL,LAG(LEVEL) OVER(ORDER BY LEVEL) lag,LEAD(LEVEL) OVER(ORDER BY LEVEL) lead
FROM DUAL CONNECT BY ROWNUM <= 5;
生成結果:
ROWNUM LEVEL LAG LEAD
---------- ---------- ---------- ----------
1 1 2
2 2 1 3
3 3 2 4
4 4 3 5
5 5 4
隨機生成 5 條數據
SELECT SYS_GUID() ora_guid,LEVEL inc_number,fn_now+LEVEL/24/3600 inc_date,
DBMS_RANDOM.STRING('X',8) random_string,
TRUNC(DBMS_RANDOM.VALUE(0,100000)) random_number
FROM DUAL CONNECT BY LEVEL <= 5;
生成結果:
ORA_GUID INC_NUMBER INC_DATE RANDOM_STRING RANDOM_NUMBER
-------------------------------- ---------- ----------- -------------- -------------
44F51C83A2964B1F81C60DBBA8BD7206 1 2017-01-10 ESL1LWPB 83888
6336F565364849889FBDC817B761E315 2 2017-01-10 7HNXAHUR 14726
C0B2BF9E4AAB4B95B9F1CB03A0582097 3 2017-01-10 ZZO8OAK3 82615
82C4A6F29BCA46BE89CA797D74F391C6 4 2017-01-10 K8ZNTRS8 54534
38F0552F1C724805A4D3E6AD54DEB43D 5 2017-01-10 4ZHL52OA 95298
構造等差數列
SELECT LEVEL n1, LEVEL*2 n2, LEVEL*2-1 n3, fn_today+LEVEL-1 dt1,
fn_today+NUMTOYMINTERVAL(LEVEL-1,'month') dt2,
fn_today+NUMTOYMINTERVAL(LEVEL-1,'year') dt3
FROM DUAL CONNECT BY LEVEL <= 5;
構造結果:
N1 N2 N3 DT1 DT2 DT3
---------- ---------- ---------- ----------- ----------- -----------
1 2 1 2017-01-10 2017-01-10 2017-01-10
2 4 3 2017-01-11 2017-02-10 2018-01-10
3 6 5 2017-01-12 2017-03-10 2019-01-10
4 8 7 2017-01-13 2017-04-10 2020-01-10
5 10 9 2017-01-14 2017-05-10 2021-01-10
構造二階等差數列
SELECT LEVEL num,SUM(LEVEL) OVER(ORDER BY LEVEL) fac FROM DUAL CONNECT BY LEVEL <= 5;
構造結果:
NUM FAC
---------- ----------
1 1
2 3
3 6
4 10
5 15
1.2、批量插入數據
一次向 t3 表中插入 100 萬條數據(在本人筆記本上操作耗時二十幾秒)
BEGIN
FOR i IN 1 .. 1000000 LOOP
INSERT INTO t3(f1) VALUES(i);
END LOOP;
COMMIT;
END;
將 t2 表中的數據變成當前的 2^5 倍(假如 t2 中原本有 3 條數據,那麼最終 t2 表中的行數將是:3+(3*2^0)+(3*2^1)+(3*2^2)+(3*2^3)+(3*2^4)
,即 3*2^5)
BEGIN
FOR i IN 1 .. 5 LOOP
INSERT INTO t2 SELECT t2.* FROM t2;
END LOOP;
COMMIT;
END;
2、批量生成腳本
生成清空當前用戶所有表中數據的語句
SELECT 'DELETE FROM '||LOWER(USER)||'.'||LOWER(t.table_name)||' t;' FROM USER_TABLES t;
SELECT 'TRUNCATE TABLE '||LOWER(USER)||'.'||LOWER(t.table_name)||';' FROM USER_TABLES t;
生成刪除當前用戶所有表的語句
SELECT 'DROP TABLE '||LOWER(USER)||'.'||LOWER(t.table_name)||' PURGE;' FROM USER_TABLES t;
生成刪除當前用戶所有對象的語句
SELECT 'DROP '||t.object_type||' '||LOWER(USER)||'.'||LOWER(t.object_name)||';'
FROM USER_OBJECTS t ORDER BY t.object_type;
生成禁用當前用戶所有觸發器的語句
SELECT 'ALTER TRIGGER '||LOWER(USER)||'.'||LOWER(t.trigger_name)||' DISABLE;'
FROM USER_TRIGGERS t WHERE t.status='ENABLED';
生成將當前用戶所有 T_ 開頭的表授權給 fox 的語句
SELECT 'GRANT SELECT ON '||LOWER(USER)||'.'||LOWER(t.table_name)||' TO fox;'
FROM USER_TABLES t WHERE t.table_name LIKE 'T/_%' ESCAPE '/';
生成查詢T_COURSE
表中數據的語句
SELECT 'SELECT '||WM_CONCAT('t.'||LOWER(t.column_name))
||' FROM '||LOWER(USER)||'.'||LOWER(MAX(t.table_name))||' t WHERE 1=1;'
FROM USER_TAB_COLUMNS t WHERE t.table_name='T_COURSE';
生成語句:
SELECT t.course_id,t.course_name,t.course_desc FROM demo.t_course t WHERE 1=1;
生成向T_COURSE
表中插入數據的語句
風格一:
SELECT 'INSERT INTO '||LOWER(USER)||'.'||LOWER(MAX(t.table_name))
||'('||WM_CONCAT(LOWER(t.column_name))
||') VALUES('||WM_CONCAT(':'||LOWER(t.column_name))||');'
FROM USER_TAB_COLUMNS t WHERE t.table_name='T_COURSE';
生成語句:
INSERT INTO demo.t_course(course_id,course_name,course_desc)
VALUES(:course_id,:course_name,:course_desc);
風格二:
SELECT 'INSERT INTO '||LOWER(USER)||'.'||LOWER(MAX(t.table_name))
||'('||WM_CONCAT(LOWER(t.column_name))
||') VALUES('||WM_CONCAT(':'||REPLACE(INITCAP(t.column_name),'_',''))||');'
FROM USER_TAB_COLUMNS t WHERE t.table_name='T_COURSE';
生成語句:
INSERT INTO demo.t_course(course_id,course_name,course_desc)
VALUES(:CourseId,:CourseName,:CourseDesc);
生成修改T_COURSE
表中數據的語句
風格一:
SELECT 'UPDATE '||LOWER(USER)||'.'||LOWER(MAX(t.table_name))
||' t SET '||WM_CONCAT('t.'||LOWER(t.column_name)||'=:'
||LOWER(t.column_name))||' WHERE 1=1;'
FROM USER_TAB_COLUMNS t WHERE t.table_name='T_COURSE';
生成語句:
UPDATE demo.t_course t
SET t.course_id=:course_id,t.course_name=:course_name,t.course_desc=:course_desc
WHERE 1=1;
風格二:
SELECT 'UPDATE '||LOWER(USER)||'.'||LOWER(MAX(t.table_name))
||' t SET '||WM_CONCAT('t.'||LOWER(t.column_name)||'=:'
||REPLACE(INITCAP(t.column_name),'_',''))||' WHERE 1=1;'
FROM USER_TAB_COLUMNS t WHERE t.table_name='T_COURSE';
生成語句:
UPDATE demo.t_course t
SET t.course_id=:CourseId,t.course_name=:CourseName,t.course_desc=:CourseDesc
WHERE 1=1;
3、生成數據字典
查詢語句:
SELECT t1.tablespace_name "表空間",USER "模式",t1.table_name "表名",
NULL "欄位序號",NULL "欄位名稱",NULL "數據類型",NULL "欄位長度",
NULL "精度",NULL "小數位",NULL "能否為空",NULL "預設值",t2.comments "註釋"
FROM USER_TABLES t1
LEFT JOIN USER_TAB_COMMENTS t2 ON t1.table_name=t2.table_name
UNION ALL
SELECT t1.tablespace_name "表空間",USER "模式",t1.table_name "表名",
t3.column_id "欄位序號",t3.column_name "欄位名稱",t3.data_type "數據類型",
t3.data_length "欄位長度",t3.data_precision "精度",t3.data_scale "小數位",
t3.nullable "能否為空",t3.data_default "預設值",t2.comments "註釋"
FROM USER_TABLES t1
LEFT JOIN USER_COL_COMMENTS t2 ON t1.table_name=t2.table_name
LEFT JOIN USER_TAB_COLUMNS t3 ON t1.table_name=t3.table_name AND t2.column_name=t3.column_name
ORDER BY "表名","欄位序號" NULLS FIRST;
查詢結果(限於篇幅,這裡僅截取了部分結果集):
表空間 模式 表名 欄位序號 欄位名稱 數據類型 欄位長度 精度 小數位 能否為空 預設值 註釋
------- ----- --------- -------- ------------ --------- -------- ----- ------ -------- ------ ------------
USERS DEMO T_COURSE
USERS DEMO T_COURSE 1 COURSE_ID NUMBER 22 10 0 N 課程主鍵ID
USERS DEMO T_COURSE 2 COURSE_NAME VARCHAR2 50 Y 課程名稱
USERS DEMO T_COURSE 3 COURSE_DESC VARCHAR2 2000 Y 課程描述
......
註意:這裡有一個非常有意思的現象,通過 PL/SQL Developer 查詢得到的結果集中,預設值data_default
欄位是 LONG 類型的。看到這個之後我曾想在查詢語句將其轉換成字元串,後來發現 Oracle 並未提供 LONG 類型轉字元類型的函數或語法,非要轉的話還得自己寫函數,總之相當繁瑣。後來我發現如果通過 PL/SQL Developer 的結果集視窗直接把數據導出到 Excel 之後,預設值列會自動轉換成字元串。再後來我又發現通過命令視窗執行查詢語句也會自動把預設值列自動轉換成字元串。總之一句話,不用自己費心費力的去轉換 LONG 類型了,直接通過 PL/SQL Developer 生成數據字典即可。
4、常見系統包
為了便於開發 PL/SQL 程式,Oracle 資料庫提供了數以百計的系統包。本機將會重點講解其中幾個常見系統包及常用方法。
4.1、DBMS_OUTPUT
DBMS_OUTPUT
包的主要功能就是在 PL/SQL 程式中輸入或輸出消息,譬如可以通過它在存儲過程和觸發器中向緩衝區發送調試消息。
常用子程式的語法及說明:
DBMS_OUTPUT.PUT_LINE(item IN VARCHAR2); -- 向緩衝區輸出數據並換行
DBMS_OUTPUT.PUT(item IN VARCHAR2); -- 向緩衝區追加數據,但不換行也不顯示,執行 NEW_LINE 或 PUT_LINE 就能把之前的數據全都顯示出來
DBMS_OUTPUT.NEW_LINE; -- 向緩衝區輸出一個換行
DBMS_OUTPUT.DISABLE; -- 用於關閉輸入和輸出,同時清空緩衝區
DBMS_OUTPUT.ENABLE([buffer_size IN NUMBER]); -- 用於開啟輸入和輸出
綜合示例:
BEGIN
DBMS_OUTPUT.PUT_LINE('A'); -- 輸出 A
DBMS_OUTPUT.DISABLE; -- 禁用 DBMS_OUTPUT 並清除 A
DBMS_OUTPUT.PUT('B'); -- 因為已關閉輸出,所以不會追加 B
DBMS_OUTPUT.ENABLE; -- 啟用 DBMS_OUTPUT
DBMS_OUTPUT.PUT('C'); -- 追加 C
DBMS_OUTPUT.PUT('D'); -- 追加 D
DBMS_OUTPUT.NEW_LINE; -- 輸出 CD 並換行
DBMS_OUTPUT.PUT_LINE('E'); -- 輸出 E 並換行
DBMS_OUTPUT.PUT('F'); -- 追加 F,但後面沒有 NEW_LINE 或 PUT_LINE,所以不會顯示
END;
輸出結果:
CD
E
4.2、DBMS_RANDOM
DBMS_RANDOM
包提供了一個內置的隨機數生成器,可用它來快速生成隨機數和隨機字元串。
RANDOM:返回一個 [-2^31, 2^31) 範圍內的整數。
SELECT DBMS_RANDOM.RANDOM res FROM DUAL; -- res: -699438152
NORMAL:返回正態分佈中的隨機數。此正態分佈標準偏差為 1,期望值為 0。這個函數返回的數值中有 68% 是介於 -1 與 +1 之間,95% 介於 -2 與 +2 之間,99% 介於 -3 與 +3 之間。
SELECT DBMS_RANDOM.NORMAL res FROM DUAL; -- res: 0.763005475791809
STRING(opt IN CHAR,len IN NUMBER):返回一個隨機字元串,其中 opt 指的是字元串的格式,len 指的是字元串的長度。
SELECT DBMS_RANDOM.STRING('u', 10) res FROM DUAL; -- res: ADKXBWIOMI,全大寫字母
SELECT DBMS_RANDOM.STRING('l', 10) res FROM DUAL; -- res: mupmuqdoue,全小寫字母
SELECT DBMS_RANDOM.STRING('a', 10) res FROM DUAL; -- res: AdOhEwGByt,混合大小寫字母
SELECT DBMS_RANDOM.STRING('x', 10) res FROM DUAL; -- res: OMUBEPN3C2,大寫字母或數字
SELECT DBMS_RANDOM.STRING('p', 10) res FROM DUAL; -- res: b+[5$ot=w|,任意可列印字元
VALUE:返回 [0, 1) 範圍內的隨機數,精度為 38 位。
SELECT DBMS_RANDOM.VALUE res FROM DUAL; -- res: 0.381593460771342
VALUE(low IN NUMBER,high IN NUMBER):返回 [low, high) 範圍內的隨機數。
SELECT DBMS_RANDOM.VALUE(10,20) res FROM DUAL; -- res: 13.650786652248
INITIALIZE(val IN BINARY_INTEGER) & SEED(seed IN BINARY_INTEGER|VARCHAR2):設置用來初始化DBMS_RANDOM
包的種子值。INITIALIZE 和 SEED 唯一的區別就是,INITIALIZE 只支持數字,而 SEED 既支持數字又支持字元串。另外,SEED 的作用之一是用來取代 INITIALIZE 的。
在預設情況下,DBMS_RANDOM
包是根據用戶、時間、會話等信息來進行初始化的,換句話說,即便是同一個語句,每次生成時的種子也是不確定的。這時候就可以通過 INITIALIZE 或 SEED 來設置一個固定的種子,確保每次生成時的隨機序列一致。
BEGIN
DBMS_RANDOM.SEED('ABC123'); -- 設置種子值 ABC123
FOR i IN 3 .. 9 LOOP
DBMS_OUTPUT.PUT(DBMS_RANDOM.RANDOM||'|');
END LOOP;
DBMS_OUTPUT.NEW_LINE;
END;
輸出結果:
-219386465|-850200733|-240588365|-351313939|-1206831363|852217108|-1045006337|
4.3、其它系統包及常用方法
DBMS_METADATA
包中的GET_DDL
方法用於獲取存儲在數據字典中的對象定義語句(DDL 語句),返回值是 CLOB 類型的。
語法:
DBMS_METADATA.GET_DDL(
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL'
);
示例:
-- 查詢定義 T_COURSE 表的 DDL 語句
SELECT DBMS_METADATA.GET_DDL('TABLE','T_COURSE') FROM DUAL;
-- 查詢定義 V_STAFF 視圖的 DDL 語句
SELECT DBMS_METADATA.GET_DDL('VIEW','V_STAFF') FROM DUAL;
-- 查詢定義 SP_STAFF_STATUS 存儲過程的 DDL 語句
SELECT DBMS_METADATA.GET_DDL('PROCEDURE','SP_STAFF_STATUS') FROM DUAL;
-- 查詢定義 TRG_STAFF_ID 觸發器的 DDL 語句
SELECT DBMS_METADATA.GET_DDL('TRIGGER','TRG_STAFF_ID') FROM DUAL;
DBMS_LOB
包中的SUBSTR
方法用於從指定偏移量截取 CLOB 類型的值並以字元串形式返回。
語法:
DBMS_LOB.SUBSTR(
lob_loc IN CLOB CHARACTER SET ANY_CS,
amount IN INTEGER := 32767,
offset IN INTEGER := 1
);
示例:
SELECT DBMS_LOB.SUBSTR(DBMS_METADATA.GET_DDL('TABLE','T_COURSE')) FROM DUAL;
DBMS_DDL
包中的ALTER_COMPILE
方法用於編譯數據中指定模式對象。
語法:
DBMS_DDL.ALTER_COMPILE(
type VARCHAR2,
schema VARCHAR2,
name VARCHAR2
reuse_settings BOOLEAN := FALSE
);
示例:
BEGIN
DBMS_DDL.ALTER_COMPILE('PROCEDURE','DEMO','SP_STAFF_STATUS');
END;
在本系列博客之前的文章中曾陸續提到DBMS_JOB
、DBMS_SCHEDULER
、DBMS_XPLAN
、DBMS_SQL
等系統包,事實上在 Oracle 中還有很多功能強大的系統包。例如,可以通過UTL_FILE
系統包讀寫操作系統文本文件,甚至可以用UTL_HTTP
系統包通過 HTTP 訪問互聯網上的數據,把指定的網頁的內容摘取下來。想要挖掘更多實用系統包的園友可以看看《Oracle Database PL/SQL Packages and Types Reference》,這個是有關 Oracle 10g 中系統包的全面介紹手冊。
5、總結
本文主要講述了利用層次查詢實現批量生成數據、利用迴圈實現批量插入數據、利用數據字典實現批量生成腳本和生成數據字典以及運用系統包等技巧。
本文鏈接:http://www.cnblogs.com/hanzongze/p/Oracle-plsql-4.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!