.Net程序員學用Oracle系列(29):PLSQL 之批量應用和系統包


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 萬條數據

循環寫法(在本人筆記本上操作耗時20幾秒)

BEGIN
  FOR i IN 1 .. 1000000 LOOP
    INSERT INTO t3(f1) VALUES(i);
  END LOOP;
  COMMIT;
END;

集合寫法(在本人筆記本上操作耗時3點幾秒)

BEGIN
  INSERT INTO t3(f1) SELECT LEVEL FROM DUAL CONNECT BY LEVEL<=1000000;
  COMMIT;
END;

直接路徑(在本人筆記本上操作耗時1點幾秒)

CREATE TABLE t4 AS SELECT LEVEL f1 FROM DUAL CONNECT BY LEVEL<=1000000;

並行寫法(在本人筆記本上操作耗時0點幾秒)

CREATE TABLE t5 NOLOGGING PARALLEL 4 AS SELECT LEVEL f1 FROM DUAL CONNECT BY LEVEL<=1000000;

將 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(t1.column_name))
  ||' FROM '||LOWER(USER)||'.'||LOWER(MAX(t1.table_name))
  ||' t WHERE t.'||LOWER(MAX(t2.column_name))||'=:'||LOWER(MAX(t2.column_name))
FROM USER_TAB_COLUMNS t1,USER_TAB_COLUMNS t2
WHERE t1.table_name=t2.table_name AND t2.column_id=1 AND t1.table_name=UPPER('t_course');

生成的語句:

SELECT t.course_id,t.course_name,t.course_desc FROM demo.t_course t WHERE t.course_id=:course_id

生成向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=UPPER('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=UPPER('t_course');

生成的語句:

INSERT INTO demo.t_course(course_id,course_name,course_desc) VALUES(:CourseId,:CourseName,:CourseDesc)

生成修改T_COURSE表中數據的語句

風格一(將 column_id=1 的字段做為條件):

SELECT 'UPDATE '||LOWER(USER)||'.'||LOWER(MAX(t1.table_name))
  ||' t SET '||WM_CONCAT('t.'||LOWER(t1.column_name)||'=:'||LOWER(t1.column_name))
  ||' WHERE t.'||LOWER(MAX(t2.column_name))||'=:'||LOWER(MAX(t2.column_name))
FROM USER_TAB_COLUMNS t1,USER_TAB_COLUMNS t2
WHERE t1.table_name=t2.table_name AND t1.column_id>1 AND t2.column_id=1
AND t1.table_name=UPPER('t_course');

生成的語句:

UPDATE demo.t_course t SET t.course_name=:course_name,t.course_desc=:course_desc WHERE t.course_id=:course_id

風格二(將 column_id=1 的字段做為條件):

SELECT 'UPDATE '||LOWER(USER)||'.'||LOWER(MAX(t1.table_name))||' t SET '
  ||WM_CONCAT('t.'||LOWER(t1.column_name)||'=:'||REPLACE(INITCAP(t1.column_name),'_',''))
  ||' WHERE t.'||LOWER(MAX(t2.column_name))||'=:'||LOWER(MAX(t2.column_name))
FROM USER_TAB_COLUMNS t1,USER_TAB_COLUMNS t2
WHERE t1.table_name=t2.table_name AND t1.column_id>1 AND t2.column_id=1
AND t1.table_name=UPPER('t_course');

生成的語句:

UPDATE demo.t_course t SET t.course_name=:CourseName,t.course_desc=:CourseDesc WHERE t.course_id=:course_id

生成添加或修改T_COURSE表注釋的語句

SELECT 'COMMENT ON TABLE '||LOWER(t1.table_name)||' IS '''||t1.comments||''';' sqltext
FROM USER_TAB_COMMENTS t1 WHERE t1.table_name=UPPER('t_course')
UNION ALL
SELECT 'COMMENT ON COLUMN '||LOWER(t.table_name)||'.'||LOWER(t.column_name)||' IS '''||t.comments||''';'
FROM USER_COL_COMMENTS t WHERE t.table_name=UPPER('t_course');

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方法用於從指定偏移量截取 LOB 類型的值並以字符串形式返回,COMPARE方法用於比較兩個 LOB 類型並以數字形式返回,值相等時返回 0,否則返回其他值。

截取語法:

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_LOB.COMPARE(
  lob_1    IN CLOB CHARACTER SET ANY_CS,
  lob_2    IN CLOB CHARACTER SET lob_1%CHARSET,
  amount   IN INTEGER := 4294967295,
  offset_1 IN INTEGER := 1,
  offset_2 IN INTEGER := 1
);

比較示例(該示例也能查出數據,說明DBMS_LOB.COMPARE函數會自動把字符類型轉換成 LOB 類型):

SELECT * FROM demo.t_course t WHERE DBMS_LOB.COMPARE(t.course_name,'語文')=0;

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_JOBDBMS_SCHEDULERDBMS_XPLANDBMS_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
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM