最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Oracle PL/SQL 從入門到精通

 更新時間:2026年04月20日 09:36:58   作者:Chenxi Hu  
文章介紹了PL/SQL語言的基本概念、特點、與SQL的區(qū)別、應用場景和語法結構,PL/SQL是Oracle數(shù)據(jù)庫中用于編寫復雜業(yè)務邏輯的程序化擴展語言,而Oracle APEX則是一個基于PL/SQL的低代碼Web應用開發(fā)平臺

PL/SQL 簡介

什么是PL/SQL?

PL/SQL(Procedural Language/Structured Query Language)是Oracle數(shù)據(jù)庫的過程化擴展語言,它將SQL的數(shù)據(jù)操作能力與過程化語言的流程控制能力相結合。PL/SQL允許開發(fā)者在數(shù)據(jù)庫服務器端編寫復雜的業(yè)務邏輯,減少網(wǎng)絡傳輸,提高應用程序性能。

PL/SQL 的主要特點

塊結構

-- PL/SQL程序由塊組成
[DECLARE]
  -- 聲明部分
BEGIN
  -- 執(zhí)行部分
[EXCEPTION]
  -- 異常處理部分
END;

過程化結構

PL/SQL支持完整的過程化編程結構:

  • 條件判斷(IF-THEN-ELSE,CASE)
  • 循環(huán)(LOOP,WHILE,F(xiàn)OR)
  • 順序控制(GOTO,NULL)

錯誤處理機制

EXCEPTION
  WHEN exception1 THEN
    -- 處理特定異常
  WHEN OTHERS THEN
    -- 處理其他所有異常

高性能特性

  • 減少網(wǎng)絡流量:在數(shù)據(jù)庫服務器端執(zhí)行復雜邏輯
  • 預編譯:代碼在首次執(zhí)行時編譯,后續(xù)執(zhí)行使用編譯后的版本
  • 批量處理:支持BULK COLLECT和FORALL進行批量操作

可移植性

PL/SQL代碼可以在所有支持Oracle數(shù)據(jù)庫的平臺上運行,無需修改。

PL/SQL與SQL的區(qū)別

特性SQLPL/SQL
語言類型聲明式語言過程化語言
執(zhí)行方式單條語句執(zhí)行塊執(zhí)行
流程控制完整的流程控制
錯誤處理有限強大的異常處理
變量支持支持變量和常量

PL/SQL的應用場景

  • 數(shù)據(jù)庫觸發(fā)器
  • 存儲過程和函數(shù)
  • 包(Package)
  • 數(shù)據(jù)庫作業(yè)(DBMS_JOB)
  • 復雜的業(yè)務邏輯實現(xiàn)

Oracle APEX

Oracle APEX簡介

Oracle APEX(Oracle Application Express)是Oracle推出的低代碼Web應用開發(fā)平臺。

核心優(yōu)勢

  • 低代碼開發(fā):通過可視化界面(如App Builder)和SQL/PL/SQL語言,大幅減少代碼量,非專業(yè)開發(fā)人員也能快速上手,顯著縮短應用開發(fā)周期。
  • 深度集成Oracle數(shù)據(jù)庫:作為Oracle生態(tài)的核心組件,與Oracle數(shù)據(jù)庫(包括自治數(shù)據(jù)庫)無縫集成,充分利用數(shù)據(jù)庫的性能、安全性、事務管理能力,適合構建數(shù)據(jù)密集型應用(如ERP、CRM、報表系統(tǒng)等)。

**核心功能模塊 **

  • App Builder:可視化拖拽式界面,支持頁面設計、表單創(chuàng)建、報表生成、流程定義等,快速構建Web應用的前端與業(yè)務邏輯。
  • SQL Workshop:Object Browser、SQL Commands等工具,用于數(shù)據(jù)庫對象管理、SQL/PL/SQL語句執(zhí)行、腳本管理等,是“數(shù)據(jù)庫到應用”的橋梁。

Oracle APEX - SQL Workshop - SQL Commands第一個PL/SQL程序

-- 簡單的PL/SQL塊
BEGIN
  DBMS_OUTPUT.PUT_LINE('Hello, PL/SQL World!');
  DBMS_OUTPUT.PUT_LINE('當前時間: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
END;
/
-- “/” 是 PL/SQL 塊特有的執(zhí)行指令,用來明確告訴 Oracle 執(zhí)行環(huán)境:“當前編輯的 PL/SQL 塊已完整,請執(zhí)行它”。
-- 帶變量的PL/SQL塊
DECLARE
  v_message VARCHAR2(100) := '歡迎學習PL/SQL';
  v_counter NUMBER := 1;
BEGIN
  WHILE v_counter <= 5 LOOP
    DBMS_OUTPUT.PUT_LINE(v_counter || ': ' || v_message);
    v_counter := v_counter + 1;
  END LOOP;
END;
/

PL/SQL 塊結構

PL/SQL 塊的基本組成

完整塊結構

[DECLARE]
  -- 聲明部分:變量、常量、游標、異常等
BEGIN
  -- 執(zhí)行部分:PL/SQL和SQL語句
[EXCEPTION]
  -- 異常處理部分:錯誤處理邏輯
END;

各部分的詳細說明

聲明部分(DECLARE)

DECLARE
  -- 變量聲明
  v_employee_id    NUMBER(6) := 100;
  v_employee_name  VARCHAR2(50);
  v_salary         NUMBER(8,2);
  v_hire_date      DATE;
  v_is_active      BOOLEAN := TRUE;
  
  -- 常量聲明
  c_company_name   CONSTANT VARCHAR2(30) := '甲骨文公司';
  c_tax_rate       CONSTANT NUMBER := 0.1;
  
  -- 異常聲明
  e_salary_too_high EXCEPTION;   -- 聲明一個自定義異常
  -- 將自定義異常與特定錯誤編號綁定
  -- -20001是 Oracle 預留的用戶自定義錯誤編號(范圍:-20000 到 -20999),用于區(qū)分系統(tǒng)錯誤(如 ORA-00001 是主鍵沖突)和用戶業(yè)務錯誤
  PRAGMA EXCEPTION_INIT(e_salary_too_high, -20001);

執(zhí)行部分(BEGIN)

BEGIN
  -- 數(shù)據(jù)查詢
  SELECT first_name || ' ' || last_name, salary, hire_date
  INTO v_employee_name, v_salary, v_hire_date
  FROM employees
  WHERE employee_id = v_employee_id;
  -- 數(shù)據(jù)處理
  IF v_salary > 10000 THEN
    RAISE e_salary_too_high;
  END IF;
  -- 數(shù)據(jù)輸出
  DBMS_OUTPUT.PUT_LINE('員工: ' || v_employee_name);
  DBMS_OUTPUT.PUT_LINE('薪資: ' || v_salary);
  DBMS_OUTPUT.PUT_LINE('入職日期: ' || TO_CHAR(v_hire_date, 'YYYY-MM-DD'));

異常處理部分(EXCEPTION)

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('錯誤: 未找到指定的員工記錄');
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('錯誤: 查詢返回了多條記錄');
  WHEN e_salary_too_high THEN
    DBMS_OUTPUT.PUT_LINE('錯誤: 員工薪資超過限制');
  WHEN OTHERS THEN
    -- SQLCODE:返回當前錯誤的錯誤編號(如系統(tǒng)錯誤NO_DATA_FOUND對應100,用戶自定義錯誤-20001等)。
    -- SQLERRM:返回與SQLCODE對應的錯誤描述信息(如ORA-01403: 未找到數(shù)據(jù)、ORA-20001: 工資過高等)。
    DBMS_OUTPUT.PUT_LINE('系統(tǒng)錯誤: ' || SQLCODE || ' - ' || SQLERRM);
    -- 事務回滾
    ROLLBACK;
END;

匿名塊詳解

匿名塊的特點

  • 沒有名稱,不被數(shù)據(jù)庫存儲
  • 每次執(zhí)行都需要重新編譯
  • 適合一次性任務和測試
  • PL/SQL 匿名塊的固定語法是 DECLARE...BEGIN...END;/,沒有任何 “命名定義”(如存儲過程的CREATE PROCEDURE 名稱

匿名塊的使用場景

-- 場景1:數(shù)據(jù)驗證和清理
DECLARE
  v_invalid_count NUMBER;
BEGIN
  -- 統(tǒng)計無效數(shù)據(jù)
  SELECT COUNT(*) INTO v_invalid_count
  FROM employees
  WHERE department_id NOT IN (SELECT department_id FROM departments);
  DBMS_OUTPUT.PUT_LINE('發(fā)現(xiàn) ' || v_invalid_count || ' 條無效記錄');
  -- 清理無效數(shù)據(jù)
  IF v_invalid_count > 0 THEN
    DELETE FROM employees
    WHERE department_id NOT IN (SELECT department_id FROM departments);
    DBMS_OUTPUT.PUT_LINE('已清理 ' || SQL%ROWCOUNT || ' 條記錄');
    COMMIT;
  END IF;
END;
/
-- 場景2:數(shù)據(jù)轉換
DECLARE
  CURSOR c_employees IS
    SELECT employee_id, salary
    FROM employees
    WHERE salary < 3000;
  v_raise_percentage NUMBER := 0.1;
BEGIN
  FOR emp_rec IN c_employees LOOP
    UPDATE employees
    SET salary = salary * (1 + v_raise_percentage)
    WHERE employee_id = emp_rec.employee_id;
    DBMS_OUTPUT.PUT_LINE('員工 ' || emp_rec.employee_id || 
                        ' 薪資從 ' || emp_rec.salary || 
                        ' 調整為 ' || emp_rec.salary * (1 + v_raise_percentage));
  END LOOP;
  COMMIT;
END;
/

命名塊詳解

命名塊的類型

  • 存儲過程(Procedure):執(zhí)行特定任務,不返回值
  • 函數(shù)(Function):執(zhí)行計算并返回一個值
  • 觸發(fā)器(Trigger):響應數(shù)據(jù)庫事件自動執(zhí)行
  • 包(Package):相關程序單元的集合

命名塊的優(yōu)勢

  1. 代碼重用:一次編寫,多次調用
  2. 模塊化:將復雜系統(tǒng)分解為小模塊
  3. 安全性:通過權限控制訪問
  4. 性能:預編譯,執(zhí)行效率高
  5. 維護性:集中管理,易于維護

塊的嵌套和標簽

嵌套塊

DECLARE
  v_outer_variable VARCHAR2(50) := '外部變量';
BEGIN
  DBMS_OUTPUT.PUT_LINE('外層塊: ' || v_outer_variable);
  -- 內層塊開始
  DECLARE
    v_inner_variable VARCHAR2(50) := '內部變量';
  BEGIN
    DBMS_OUTPUT.PUT_LINE('內層塊: ' || v_outer_variable); -- 可以訪問外部變量
    DBMS_OUTPUT.PUT_LINE('內層塊: ' || v_inner_variable);
    -- 可以重新定義外部變量(不推薦)
    v_outer_variable := '在內層塊修改的外部變量';
  END;
  DBMS_OUTPUT.PUT_LINE('外層塊: ' || v_outer_variable); -- 顯示修改后的值
END;
/

塊標簽

<<outer_block>>
DECLARE
  v_counter NUMBER := 1;
BEGIN
  DBMS_OUTPUT.PUT_LINE('外部塊計數(shù)器: ' || v_counter);
  <<inner_block>>
  DECLARE
    v_counter NUMBER := 100; -- 與外部塊變量同名
  BEGIN
    DBMS_OUTPUT.PUT_LINE('內部塊計數(shù)器: ' || v_counter); -- 顯示100
    DBMS_OUTPUT.PUT_LINE('外部塊計數(shù)器: ' || outer_block.v_counter); -- 顯示1
    outer_block.v_counter := outer_block.v_counter + 1; -- 修改外部塊變量
  END inner_block;
  DBMS_OUTPUT.PUT_LINE('外部塊計數(shù)器: ' || v_counter); -- 顯示2
END outer_block;
/

變量和數(shù)據(jù)類型

變量聲明

變量聲明語法

variable_name [CONSTANT] datatype [NOT NULL] [:= | DEFAULT initial_value];

變量聲明示例

DECLARE
  -- 基本變量聲明
  v_employee_id    NUMBER(6);
  v_first_name     VARCHAR2(20);
  v_last_name      VARCHAR2(25);
  v_salary         NUMBER(8,2);
  v_commission_pct NUMBER(2,2);
  v_hire_date      DATE;
  v_is_manager     BOOLEAN;
  -- 帶初始化的變量聲明
  v_department_id  NUMBER(4) := 50;
  v_job_id         VARCHAR2(10) DEFAULT 'IT_PROG';
  v_active_status  BOOLEAN := TRUE;
  -- 常量聲明
  c_company_name   CONSTANT VARCHAR2(30) := 'Oracle Corporation';
  c_max_salary     CONSTANT NUMBER := 100000;
  c_pi             CONSTANT NUMBER := 3.14159;
  -- NOT NULL約束
  v_required_field VARCHAR2(50) NOT NULL := '默認值';
BEGIN
  -- 變量使用示例
  v_employee_id := 100;
  v_first_name := 'Steven';
  v_last_name := 'King';
  v_salary := 24000;
  v_hire_date := SYSDATE;
  v_is_manager := TRUE;
  DBMS_OUTPUT.PUT_LINE('員工: ' || v_first_name || ' ' || v_last_name);
  DBMS_OUTPUT.PUT_LINE('薪資: ' || v_salary);
  DBMS_OUTPUT.PUT_LINE('公司: ' || c_company_name);
END;
/

標量數(shù)據(jù)類型

數(shù)值類型

DECLARE
  -- NUMBER類型
  v_integer      NUMBER(5);        -- 整數(shù),最多5位
  v_decimal      NUMBER(8,2);      -- 小數(shù),總位數(shù)8,小數(shù)位2
  v_float        NUMBER;           -- 浮點數(shù)
  v_scientific   NUMBER;           -- 科學計數(shù)法
  -- 整數(shù)子類型
  v_pls_integer  PLS_INTEGER;      -- PL/SQL整數(shù),性能更好
  v_binary_int   BINARY_INTEGER;   -- 二進制整數(shù)
  v_simple_int   SIMPLE_INTEGER;   -- 簡單整數(shù)(NOT NULL)
  -- 其他數(shù)值類型
  v_double       BINARY_DOUBLE;    -- 二進制雙精度
  v_float_num    BINARY_FLOAT;     -- 二進制單精度
BEGIN
  v_integer := 12345;
  v_decimal := 123456.78;
  v_float := 3.1415926535;
  v_scientific := 1.23E+5;         -- 123000
  v_pls_integer := 100;
  v_binary_int := -50;
  v_simple_int := 200;
  v_double := 3.141592653589793;
  v_float_num := 2.71828;
  DBMS_OUTPUT.PUT_LINE('整數(shù): ' || v_integer);
  DBMS_OUTPUT.PUT_LINE('小數(shù): ' || v_decimal);
  DBMS_OUTPUT.PUT_LINE('科學計數(shù): ' || v_scientific);
END;
/

字符類型

DECLARE
  -- VARCHAR2類型
  v_varchar2      VARCHAR2(50);    -- 可變長度字符串
  v_varchar2_def  VARCHAR2(100) := '默認值';
  -- CHAR類型
  v_char          CHAR(10);        -- 定長字符串,自動填充空格
  v_char_def      CHAR(5) := 'ABC';
  -- 長文本類型
  v_long          LONG;            -- 長文本(已過時)
  v_clob          CLOB;            -- 字符大對象
  -- 其他字符類型
  v_nchar         NCHAR(10);       -- 國家字符集定長
  v_nvarchar2     NVARCHAR2(50);   -- 國家字符集變長
  v_raw           RAW(100);        -- 原始二進制數(shù)據(jù)
  v_long_raw      LONG RAW;        -- 長原始二進制(已過時)
  v_blob          BLOB;            -- 二進制大對象
BEGIN
  v_varchar2 := '這是一個變長字符串';
  v_char := '定長';                -- 實際存儲為'定長      '
  v_clob := '這是一個非常大的文本內容...';
  DBMS_OUTPUT.PUT_LINE('VARCHAR2: ' || v_varchar2);
  DBMS_OUTPUT.PUT_LINE('CHAR: [' || v_char || ']'); -- 顯示填充的空格
  DBMS_OUTPUT.PUT_LINE('CLOB長度: ' || DBMS_LOB.GETLENGTH(v_clob));
END;
/

日期和時間類型

DECLARE
  -- 日期時間類型
  v_date          DATE;                    -- 日期和時間
  v_timestamp     TIMESTAMP;               -- 時間戳
  v_timestamp_tz  TIMESTAMP WITH TIME ZONE;-- 帶時區(qū)的時間戳
  v_timestamp_ltz TIMESTAMP WITH LOCAL TIME ZONE; -- 本地時區(qū)時間戳
  -- 時間間隔類型
  v_interval_ym   INTERVAL YEAR TO MONTH;  -- 年月間隔
  v_interval_ds   INTERVAL DAY TO SECOND;  -- 日秒間隔
BEGIN
  v_date := SYSDATE;
  v_timestamp := SYSTIMESTAMP;
  v_timestamp_tz := CURRENT_TIMESTAMP;
  v_timestamp_ltz := LOCALTIMESTAMP;
  v_interval_ym := INTERVAL '1-6' YEAR TO MONTH;  -- 1年6個月
  v_interval_ds := INTERVAL '5 12:30:15' DAY TO SECOND; -- 5天12小時30分15秒
  DBMS_OUTPUT.PUT_LINE('當前日期: ' || TO_CHAR(v_date, 'YYYY-MM-DD HH24:MI:SS'));
  DBMS_OUTPUT.PUT_LINE('當前時間戳: ' || v_timestamp);
  DBMS_OUTPUT.PUT_LINE('時間間隔: ' || v_interval_ym);
  DBMS_OUTPUT.PUT_LINE('日秒間隔: ' || v_interval_ds);
END;
/

布爾類型

DECLARE
  v_flag1 BOOLEAN;
  v_flag2 BOOLEAN := TRUE;
  v_flag3 BOOLEAN := FALSE;
  v_flag4 BOOLEAN := NULL;
BEGIN
  v_flag1 := (10 > 5);  -- 賦值為TRUE
  -- 布爾值的使用
  IF v_flag1 THEN
    DBMS_OUTPUT.PUT_LINE('條件1為真');
  END IF;
  IF NOT v_flag3 THEN
    DBMS_OUTPUT.PUT_LINE('條件3為假');
  END IF;
  -- 注意:布爾值不能直接輸出
  -- DBMS_OUTPUT.PUT_LINE(v_flag1); -- 這會報錯
  -- 可以通過條件判斷輸出
  DBMS_OUTPUT.PUT_LINE('flag1: ' || CASE WHEN v_flag1 THEN 'TRUE'
                                        WHEN NOT v_flag1 THEN 'FALSE'
                                        ELSE 'NULL' END);
END;
/

復合數(shù)據(jù)類型

記錄類型(RECORD)

在 Oracle 的 PL/SQL 中,RECORD(記錄)類型是一種復合數(shù)據(jù)類型,用于將多個相關但數(shù)據(jù)類型可能不同的 “字段(Field)” 組合成一個整體,類似其他編程語言中的 “結構體(Struct)”。

DECLARE
  -- 定義記錄類型
  TYPE employee_rec IS RECORD (
    employee_id    employees.employee_id%TYPE,
    first_name     employees.first_name%TYPE,
    last_name      employees.last_name%TYPE,
    salary         employees.salary%TYPE,
    hire_date      employees.hire_date%TYPE
  );
  -- 聲明記錄變量
  v_employee employee_rec;
  -- 使用%ROWTYPE定義記錄
  v_emp_row employees%ROWTYPE;
BEGIN
  -- 為記錄字段賦值
  v_employee.employee_id := 100;
  v_employee.first_name := 'Steven';
  v_employee.last_name := 'King';
  v_employee.salary := 24000;
  v_employee.hire_date := SYSDATE;
  -- 使用SELECT INTO為記錄賦值
  SELECT employee_id, first_name, last_name, salary, hire_date
  INTO v_employee
  FROM employees
  WHERE employee_id = 101;
  -- 使用%ROWTYPE
  SELECT * INTO v_emp_row
  FROM employees
  WHERE employee_id = 102;
  -- 輸出記錄內容
  DBMS_OUTPUT.PUT_LINE('員工: ' || v_employee.first_name || ' ' || v_employee.last_name);
  DBMS_OUTPUT.PUT_LINE('薪資: ' || v_employee.salary);
  DBMS_OUTPUT.PUT_LINE('ROW員工: ' || v_emp_row.first_name || ' ' || v_emp_row.last_name);
END;
/

集合類型

關聯(lián)數(shù)組(INDEX-BY TABLE)

? 在 Oracle 的 PL/SQL 中,關聯(lián)數(shù)組(Associative Array) 也稱為INDEX-BY TABLE(索引表),是一種集合類型,用于存儲多個同類型元素的 “鍵值對” 集合,類似其他編程語言中的 “哈希表(Hash Table)” 或 “字典(Dictionary)”。

DECLARE  -- 聲明區(qū):定義類型、變量等(僅聲明不執(zhí)行)
  -- 定義第一個關聯(lián)數(shù)組類型:salary_table
  -- 元素類型為NUMBER(存儲薪資),索引類型為PLS_INTEGER(整數(shù)索引)
  TYPE salary_table IS TABLE OF NUMBER
    INDEX BY PLS_INTEGER;
  -- 定義第二個關聯(lián)數(shù)組類型:name_table
  -- 元素類型為VARCHAR2(50)(存儲姓名),索引類型為VARCHAR2(10)(字符串索引,長度不超過10)
  TYPE name_table IS TABLE OF VARCHAR2(50)
    INDEX BY VARCHAR2(10);
  -- 聲明關聯(lián)數(shù)組變量:基于上面定義的類型創(chuàng)建實例
  v_salaries salary_table;  -- 用于存儲“整數(shù)索引->薪資”的關聯(lián)數(shù)組
  v_names name_table;       -- 用于存儲“字符串索引->姓名”的關聯(lián)數(shù)組
  -- 聲明遍歷用的變量:存儲當前索引/鍵
  v_index PLS_INTEGER;      -- 用于遍歷整數(shù)索引的v_salaries
  v_key VARCHAR2(10);       -- 用于遍歷字符串索引的v_names
BEGIN  -- 執(zhí)行區(qū):編寫具體邏輯(實際執(zhí)行的代碼)
  -- 為v_salaries賦值(整數(shù)索引)
  v_salaries(1) := 5000;    -- 索引1對應的值為5000
  v_salaries(2) := 7500;    -- 索引2對應的值為7500
  v_salaries(5) := 10000;   -- 索引5對應的值為10000(演示:關聯(lián)數(shù)組索引可以不連續(xù))
  -- 為v_names賦值(字符串索引)
  v_names('emp001') := '張三';   -- 索引'emp001'對應的值為'張三'
  v_names('emp002') := '李四';   -- 索引'emp002'對應的值為'李四'
  v_names('manager') := '王經(jīng)理';-- 索引'manager'對應的值為'王經(jīng)理'
  -- 遍歷v_salaries(整數(shù)索引的關聯(lián)數(shù)組)
  v_index := v_salaries.FIRST;  -- 獲取v_salaries的第一個索引(此處為1)
  -- 循環(huán)條件:當前索引不為空(即還有元素未遍歷)
  WHILE v_index IS NOT NULL LOOP
    -- 輸出當前索引和對應的值
    DBMS_OUTPUT.PUT_LINE('索引 ' || v_index || ': ' || v_salaries(v_index));
    v_index := v_salaries.NEXT(v_index);  -- 獲取當前索引的下一個索引(1的下一個是2,2的下一個是5,5的下一個為空)
  END LOOP;  -- 結束循環(huán)
  -- 遍歷v_names(字符串索引的關聯(lián)數(shù)組)
  v_key := v_names.FIRST;  -- 獲取v_names的第一個鍵(字符串索引按字典序排序,此處為'emp001')
  -- 循環(huán)條件:當前鍵不為空(即還有元素未遍歷)
  WHILE v_key IS NOT NULL LOOP
    -- 輸出當前鍵和對應的值
    DBMS_OUTPUT.PUT_LINE('鍵 ' || v_key || ': ' || v_names(v_key));
    v_key := v_names.NEXT(v_key);  -- 獲取當前鍵的下一個鍵('emp001'的下一個是'emp002',再下一個是'manager',最后為空)
  END LOOP;  -- 結束循環(huán)
  -- 檢查v_salaries中是否存在索引3的元素
  IF v_salaries.EXISTS(3) THEN  -- EXISTS(索引):判斷指定索引是否存在
    DBMS_OUTPUT.PUT_LINE('索引3存在');
  ELSE
    DBMS_OUTPUT.PUT_LINE('索引3不存在');  -- 此處因未給索引3賦值,會執(zhí)行該分支
  END IF;
  -- 獲取并輸出關聯(lián)數(shù)組的元素數(shù)量(COUNT方法)
  DBMS_OUTPUT.PUT_LINE('v_salaries元素數(shù)量: ' || v_salaries.COUNT);  -- 共3個元素(索引1、2、5)
  DBMS_OUTPUT.PUT_LINE('v_names元素數(shù)量: ' || v_names.COUNT);        -- 共3個元素(鍵'emp001'、'emp002'、'manager')
END;  -- 結束PL/SQL塊
/  -- 執(zhí)行該PL/SQL塊

嵌套表(NESTED TABLE)

? 在Oracle數(shù)據(jù)庫中,嵌套表(Nested Table) 是一種用戶定義的集合數(shù)據(jù)類型,它允許將整個表作為另一張表的列進行存儲。

嵌套表是一種:

  • 無序的數(shù)據(jù)集合(元素沒有固定順序)
  • 可以動態(tài)擴展(元素數(shù)量不固定)
  • 存儲在數(shù)據(jù)庫表中的特殊列類型
  • 實際數(shù)據(jù)存儲在單獨的存儲表中
DECLARE  -- 聲明區(qū):定義類型、變量(僅聲明不執(zhí)行)
  -- 定義第一個嵌套表類型:number_table,元素類型為NUMBER(存儲數(shù)字)
  TYPE number_table IS TABLE OF NUMBER;
  -- 定義第二個嵌套表類型:string_table,元素類型為VARCHAR2(50)(存儲字符串,長度不超過50)
  TYPE string_table IS TABLE OF VARCHAR2(50);
  -- 聲明嵌套表變量(基于上面定義的類型)
  -- v_numbers:number_table類型的變量,通過構造函數(shù)number_table()初始化(創(chuàng)建空嵌套表,非NULL)
  v_numbers number_table := number_table(); -- 嵌套表必須初始化,否則操作會報錯
  -- v_names:string_table類型的變量,聲明時直接通過構造函數(shù)賦值(初始包含3個元素:'張三'、'李四'、'王五')
  v_names string_table := string_table('張三', '李四', '王五');
  -- 聲明遍歷用的變量:存儲嵌套表的索引(整數(shù)類型)
  v_index NUMBER;
BEGIN  -- 執(zhí)行區(qū):編寫具體邏輯(實際執(zhí)行的代碼)
  -- 為v_numbers添加元素(第一種方式:先擴展容量再賦值)
  v_numbers.EXTEND(3); -- 擴展3個空元素(此時v_numbers有3個可用索引:1、2、3)
  v_numbers(1) := 100; -- 給索引1的元素賦值100
  v_numbers(2) := 200; -- 給索引2的元素賦值200
  v_numbers(3) := 300; -- 給索引3的元素賦值300
  -- 為v_numbers重新賦值(第二種方式:直接通過構造函數(shù)覆蓋原有值)
  -- 此時v_numbers的元素變?yōu)?0、20、30、40、50(索引1-5,覆蓋了之前的100、200、300)
  v_numbers := number_table(10, 20, 30, 40, 50);
  -- 遍歷v_numbers(使用FOR循環(huán),基于元素數(shù)量COUNT)
  -- 循環(huán)范圍:從1到v_numbers的元素總數(shù)(COUNT=5,即i=1→2→3→4→5)
  FOR i IN 1..v_numbers.COUNT LOOP
    -- 輸出當前索引i和對應的值
    DBMS_OUTPUT.PUT_LINE('數(shù)字[' || i || ']: ' || v_numbers(i));
  END LOOP;  -- 結束循環(huán)
  -- 遍歷v_names(使用WHILE循環(huán),基于FIRST和NEXT方法)
  v_index := v_names.FIRST; -- 獲取v_names的第一個索引(初始為1,因初始元素是按順序添加的)
  -- 循環(huán)條件:當前索引不為空(即還有元素未遍歷)
  WHILE v_index IS NOT NULL LOOP
    -- 輸出當前索引v_index和對應的值
    DBMS_OUTPUT.PUT_LINE('姓名[' || v_index || ']: ' || v_names(v_index));
    v_index := v_names.NEXT(v_index); -- 獲取當前索引的下一個索引(1→2→3→NULL)
  END LOOP;  -- 結束循環(huán)
  -- 刪除v_names中的元素(刪除索引為2的元素,即'李四')
  v_names.DELETE(2); 
  -- 輸出刪除后v_names的元素數(shù)量(原3個,刪除1個,剩余2個)
  DBMS_OUTPUT.PUT_LINE('刪除后元素數(shù)量: ' || v_names.COUNT);
END;  -- 結束PL/SQL塊
/  -- 執(zhí)行該PL/SQL塊

變長數(shù)組(VARRAY)

DECLARE  -- 聲明區(qū):定義類型、變量(僅聲明不執(zhí)行)
  -- 定義第一個VARRAY類型:number_varray
  -- VARRAY(5)表示這是一個可變數(shù)組,最大容量為5個元素;OF NUMBER表示元素類型為數(shù)字
  TYPE number_varray IS VARRAY(5) OF NUMBER; -- 最大可存儲5個元素
  -- 定義第二個VARRAY類型:name_varray
  -- VARRAY(10)表示最大容量為10個元素;OF VARCHAR2(50)表示元素為字符串(最大長度50)
  TYPE name_varray IS VARRAY(10) OF VARCHAR2(50);
  -- 聲明VARRAY變量(基于上面定義的類型)
  -- v_numbers:number_varray類型的變量,通過構造函數(shù)number_varray()初始化(創(chuàng)建空數(shù)組,非NULL)
  -- VARRAY必須初始化,否則操作會報錯
  v_numbers number_varray := number_varray(); 
  -- v_names:name_varray類型的變量,聲明時通過構造函數(shù)直接賦值(初始包含2個元素:'張三'、'李四')
  v_names name_varray := name_varray('張三', '李四');
  -- 聲明遍歷用的變量:存儲VARRAY的索引(整數(shù)類型)
  v_index NUMBER;
BEGIN  -- 執(zhí)行區(qū):編寫具體邏輯(實際執(zhí)行的代碼)
  -- 為v_numbers添加元素(先擴展容量,再賦值)
  v_numbers.EXTEND(3); -- 擴展3個空元素(此時v_numbers的容量變?yōu)?,未超過最大限制5)
  v_numbers(1) := 100; -- 給第1個元素賦值100(VARRAY索引從1開始)
  v_numbers(2) := 200; -- 給第2個元素賦值200
  v_numbers(3) := 300; -- 給第3個元素賦值300
  -- 遍歷v_numbers(使用FOR循環(huán),范圍從1到當前元素總數(shù)COUNT)
  -- 此時v_numbers.COUNT=3,即i=1→2→3
  FOR i IN 1..v_numbers.COUNT LOOP
    -- 輸出當前索引i和對應的值
    DBMS_OUTPUT.PUT_LINE('數(shù)字[' || i || ']: ' || v_numbers(i));
  END LOOP;  -- 結束循環(huán)
  -- 獲取并輸出v_numbers的最大容量(LIMIT是VARRAY特有的方法,返回定義時指定的最大元素數(shù))
  DBMS_OUTPUT.PUT_LINE('v_numbers最大容量: ' || v_numbers.LIMIT); -- 輸出5(定義時VARRAY(5))
  -- 輸出v_numbers當前的元素數(shù)量(COUNT返回實際存儲的元素數(shù))
  DBMS_OUTPUT.PUT_LINE('v_numbers當前大小: ' || v_numbers.COUNT); -- 輸出3(已添加3個元素)
  -- 操作v_names數(shù)組
  v_names.EXTEND; -- 擴展1個空元素(不指定參數(shù)時默認擴展1個,此時v_names容量從2變?yōu)?,未超過最大限制10)
  v_names(3) := '王五'; -- 給第3個元素賦值'王五'
  -- 輸出v_names當前的元素數(shù)量(擴展并賦值后,COUNT=3)
  DBMS_OUTPUT.PUT_LINE('v_names當前大小: ' || v_names.COUNT);
END;  -- 結束PL/SQL塊
/  -- 執(zhí)行該PL/SQL塊

三種集合類型對比

特性嵌套表(Nested Table)變長數(shù)組(VARRAY)關聯(lián)數(shù)組(Index-by Table)
定義用戶定義的無序集合用戶定義的有序集合PL/SQL中的鍵值對集合
存儲位置數(shù)據(jù)庫表中數(shù)據(jù)庫表中僅內存中
大小動態(tài),無限制固定上限動態(tài),無限制

選擇建議

  • 需要持久化存儲且數(shù)據(jù)量變化大 → 嵌套表
  • 數(shù)據(jù)量固定且需要保持順序 → 變長數(shù)組
  • 臨時數(shù)據(jù)處理和快速查找 → 關聯(lián)數(shù)組
  • 需要在SQL中查詢集合內容 → 嵌套表或變長數(shù)組
  • 僅PL/SQL內部使用 → 關聯(lián)數(shù)組

數(shù)據(jù)類型屬性

%TYPE屬性

DECLARE
  -- 使用%TYPE引用表列的類型
  v_employee_id   employees.employee_id%TYPE;
  v_first_name    employees.first_name%TYPE;
  v_salary        employees.salary%TYPE;
  v_hire_date     employees.hire_date%TYPE;
  -- 引用其他變量的類型
  v_bonus v_salary%TYPE;
BEGIN
  v_employee_id := 100;
  v_first_name := 'Steven';
  v_salary := 24000;
  v_hire_date := SYSDATE;
  v_bonus := v_salary * 0.1;
  DBMS_OUTPUT.PUT_LINE('員工ID: ' || v_employee_id);
  DBMS_OUTPUT.PUT_LINE('姓名: ' || v_first_name);
  DBMS_OUTPUT.PUT_LINE('薪資: ' || v_salary);
  DBMS_OUTPUT.PUT_LINE('獎金: ' || v_bonus);
END;
/

%ROWTYPE屬性

DECLARE
  -- 使用%ROWTYPE引用整行類型
  v_employee employees%ROWTYPE;
  v_department departments%ROWTYPE;
BEGIN
  -- 為記錄賦值
  SELECT * INTO v_employee
  FROM employees
  WHERE employee_id = 100;
  SELECT * INTO v_department
  FROM departments
  WHERE department_id = v_employee.department_id;
  -- 訪問記錄字段
  DBMS_OUTPUT.PUT_LINE('員工: ' || v_employee.first_name || ' ' || v_employee.last_name);
  DBMS_OUTPUT.PUT_LINE('部門: ' || v_department.department_name);
  DBMS_OUTPUT.PUT_LINE('薪資: ' || v_employee.salary);
  DBMS_OUTPUT.PUT_LINE('入職日期: ' || TO_CHAR(v_employee.hire_date, 'YYYY-MM-DD'));
END;
/

數(shù)據(jù)類型轉換

隱式轉換

DECLARE
  v_number NUMBER;
  v_varchar VARCHAR2(50);
  v_date DATE;
BEGIN
  -- 數(shù)字到字符串的隱式轉換
  v_varchar := 123.45;
  DBMS_OUTPUT.PUT_LINE('數(shù)字轉字符串: ' || v_varchar);
  -- 字符串到數(shù)字的隱式轉換
  v_number := '456.78';
  DBMS_OUTPUT.PUT_LINE('字符串轉數(shù)字: ' || v_number);
  -- 日期到字符串的隱式轉換
  v_varchar := SYSDATE;
  DBMS_OUTPUT.PUT_LINE('日期轉字符串: ' || v_varchar);
END;
/

顯式轉換

DECLARE
  v_number NUMBER := 123.456;
  v_varchar VARCHAR2(50);
  v_date DATE;
  v_timestamp TIMESTAMP;
BEGIN
  -- 使用TO_CHAR轉換數(shù)字和日期
  v_varchar := TO_CHAR(v_number, '999,999.99');
  DBMS_OUTPUT.PUT_LINE('格式化數(shù)字: ' || v_varchar);
  v_varchar := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
  DBMS_OUTPUT.PUT_LINE('格式化日期: ' || v_varchar);
  -- 使用TO_NUMBER轉換字符串為數(shù)字
  v_number := TO_NUMBER('$1,234.56', '$9,999.99');
  DBMS_OUTPUT.PUT_LINE('字符串轉數(shù)字: ' || v_number);
  -- 使用TO_DATE轉換字符串為日期
  v_date := TO_DATE('2023-12-25 14:30:00', 'YYYY-MM-DD HH24:MI:SS');
  DBMS_OUTPUT.PUT_LINE('字符串轉日期: ' || TO_CHAR(v_date, 'YYYY-MM-DD'));
  -- 使用CAST進行類型轉換
  v_timestamp := CAST(SYSDATE AS TIMESTAMP);
  DBMS_OUTPUT.PUT_LINE('CAST轉換: ' || v_timestamp);
END;
/

到此這篇關于Oracle PL/SQL 從入門到精通的文章就介紹到這了,更多相關Oracle PL/SQL全解內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

玛多县| 松阳县| 左贡县| 潢川县| 宝清县| 贵港市| 富民县| 宁夏| 昌黎县| 揭阳市| 西乌| 吉水县| 辽宁省| 常山县| 建宁县| 嘉善县| 沁水县| 阿巴嘎旗| 延边| 五原县| 介休市| 琼中| 昌都县| 两当县| 肥乡县| 沾益县| 临高县| 洛阳市| 萝北县| 安化县| 磐安县| 平江县| 称多县| 德化县| 错那县| 侯马市| 湖北省| 贡觉县| 隆子县| 静海县| 诸暨市|