Oracle 數據庫的概念與使用

目錄


Oracle

1. Oracle 概述

a. 常見數據庫

  • 數據庫(Database)是按照數據結構來組織、存儲和管理數據的倉庫。數據庫通常分爲層次式數據庫、網絡式數據庫和關係式數據庫三種;不同的數據庫是按不同的數據結構來聯繫和組織的。將反映和實現數據聯繫的方法稱爲數據模型。層次結構模型實質上是一種有根結點的定向有序樹,按照層次模型建立的數據庫系統稱爲層次模型數據庫系統;按照網狀數據結構建立的數據庫系統稱爲網狀數據庫系統;關係式數據結構把一些複雜的數據結構歸結爲簡單的二元關係(即二維表格形式),由關係數據結構組成的數據庫系統被稱爲關係數據庫系統。
  • 數據庫管理系統(Database Management System)是一種操縱和管理數據庫的大型軟件,用於建立、使用和維護數據庫,簡稱 DBMS。它對數據庫進行統一的管理和控制,以保證數據庫的安全性和完整性。用戶通過 DBMS 訪問數據庫中的數據。數據庫管理系統是數據庫系統的核心,是管理數據庫的軟件。
  • 常見的關係型數據庫有:DB2,Sybase,Oracle,MySQL,Access,MS SQL Server 等。

b. Oracle 簡介

  • Oracle 甲骨文公司是第一個跨整個產品線(數據庫、業務應用軟件和應用軟件開發與決策支持工具)開發和部署 100% 基於互聯網的企業軟件的公司。Oracle 是世界領先的信息管理軟件供應商和世界第二大獨立軟件公司。其主要的有:
    • 數據庫服務器:Oracle(9i,10g/11g,12c),MySQL
    • 應用服務器:WegLogic,GlassFish
    • 開發語言:Java
    • 開發集成環境:NetBean
    • Oracle 數據庫是當前最主流的數據庫之一。

2. Oracle 的組成

a. Oracle 11g 數據庫的組成

  • Oracle 的整體架構:
    在這裏插入圖片描述
  • 上圖示;一般 Oracle 數據庫管理系統由:實例和數據庫兩部分組成。
    • 數據庫是一系列物理文件的集合(數據文件,控制文件,聯機日誌,參數文件等);Oracle 數據庫由操作系統文件組成,這些文件也稱爲數據庫文件,爲數據庫信息提供實際物理存儲區。Oracle 數據庫包括邏輯結構和物理結構。數據庫的物理結構包含數據庫中的一組操作系統文件。數據庫的邏輯結構是指數據庫創建之後形成的邏輯概念之間的關係,如表、視圖、索引等對象。
    • 實例則是一組 Oracle 後臺進程/線程以及在服務器分配的共享內存區。
  • Oracle 可以創建多個 Oracle 數據庫,一個 Oracle 數據庫將又由實例和數據庫構成。如默認安裝時創建的 orcl 數據庫外還可再創建其它數據庫。創建的數據庫將在$OracleHome/oradata/數據庫名 目錄下以一個個的 *.DBF 文件體現出來。

b. Oracle 11g 數據庫服務

  • Oracle * VSS Writer Service – Oracle 卷映射拷貝寫入服務,VSS(Volume Shadow Copy Service)能夠讓存儲基礎設備(比如磁盤,陣列等)創建高保真的時間點映像,即映射拷貝(shadow copy)。它可以在多卷或者單個捲上創建映射拷貝,同時不會影響到系統的系統能。(非必須啓動)
  • OracleDBConsole* – Oracle 數 據 庫 控 制 臺 服 務 ; 在 運 行 Enterprise Manager(企業管理器 EM)的時候,需要啓動這個服務;此服務被默認設置爲自動開機啓動的(非必須啓動)
  • OracleJobScheduler* – Oracle 作業調度服務。此服務被默認設置爲禁用狀態(非必須啓動)
  • OracleMTSRecoveryService – 服務端控制。該服務允許數據庫充當一個微軟事務服務器 MTS、COM/COM+對象和分佈式環境下的事務的資源管理器。恢復、閃回需要開啓該服務(非必須啓動)
  • OracleOraDb11g_home1ClrAgent – Oracle 數據庫.NET 擴展服務的一部分。(非必須啓動)
  • OracleOraDb11g_home1TNSListener – 監聽器服務,服務只有在數據庫需要遠程訪問或使用 SQL Developer 等工具的時候才需要,此服務被默認的設置爲開機啓動(非必須啓動)
  • OracleService* – 數據庫服務,是 Oracle 核心服務該服務,是數據庫啓動的基礎, 只有該服務啓動,Oracle 數據庫才能正常操作。此服務被默認的設置爲開機啓動。(必須啓動)

3. 連接 Oracle

a. SQL Plus 連接

  • 打開 SQL Plus:
    在這裏插入圖片描述
  • 在上述界面中可以輸入用戶名,如在安裝時解鎖了的用戶 scott,口令爲:tiger
  • 輸入語句查詢該用戶下的對象:
    在這裏插入圖片描述
  • 另外;也可以直接在命令行中輸入 sqlplus scott/tiger 進入並登錄

b. SQL Developer 連接

  • 打開 SQL Developer ;在出現界面的左邊右擊鼠標,新建連接:
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 注意在上圖中;
    • 主機名:如果是本機的按照配置在網絡管理中的服務的配置設置,可以爲 localhost;如果是連接其它機器的數據庫則指定其 ip;
    • SID:是指定數據庫服務器上的全局數據庫名稱,默認安裝的話一般是 orcl

在這裏插入圖片描述

c. PLSQL Developer 連接

  • 安裝 PLSQL Develper;見《PLSQL Developer 安裝及註冊.docx》在這裏插入圖片描述
    在這裏插入圖片描述

d. Jdbc 連接

  1. 在安裝目錄下找到 Oracle 的驅動包;
    如下路徑可以找到 Oracle 的驅動包:
    C:\Oracle11g\product\11.2.0\dbhome_1\jdbc\lib 複製 ojdbc6.jar 到項目中進行連接測試;
  2. 新建 Java 項目測試連接;

4. SQL Plus 設置與常用命令

a. 顯示設置

-- 設置每行顯示的最長字符數
set linesize 120
-- 設置一頁顯示的行數
set pagesize 20
-- 設置是否顯示一頁的記錄數
set feedback on/off
-- 打開或取消Oracle自帶的輸出方法dbms_output,並輸出內容
set serveroutput on/off
-- 格式化列的內容:將列名對應的列的值格式化爲四位數值長度
col 表中對應的列名 for 9999
column 表中對應的列名 format 9999
【示例】
-- 表明將 empno 列名對應的列值格式爲 4 位長度的數值型
col empno for 9999 
-- 格式化列的內容:將列名對應的列的值格式化爲10位字母長度
col 表中對應的列名 for a10
【示例】
-- 表明將ename列名對應的列值格式爲10位長度的字符型
col ename for a10

b. 常用命令

命令 說明
show all 查看系統所有變量值
show user 顯示當前連接用戶
conn 切換用戶,連接系統
desc 表名 顯示錶的結構;如:desc emp
/* */ 多行註釋
單行註釋
/ 執行緩衝區中的語句
ed 打開默認編輯器,Windows 系統中默認是 notepad.exe,把緩衝區中最後一條 SQL 語句調入 afiedt.buf 文件中進行編輯(如果提示沒有afiedt.buf 請使用管理員身份打 開 SLQ Plus);常用於語句比較長需要修改時。
spool文件地址

spool 文件地址 append

spool off
假脫機命令;將命令行的內容(從設置後開始的命令行內容)記錄到文本。添加 append 的意思是在原有的文本內容上追加後續的命令行的內容;需要注意的是所有的這些內容都將在 spool off 之後才記錄。如:
spool d:\regino\regino.txt
spool d:\regino\test.sql append
spool off
clear screen 或者 host cls 清屏
exit 退出 SQL Plus

5. 表空間

  • 表空間是數據庫中最大的邏輯單位,Oracle 數據庫採用表空間將相關的邏輯組件組合在一起,一個 Oracle 數據庫至少包含一個表空間。每個表空間由一個或多個數據文件組成,一個數據文件只能與一個表空間相聯繫。
  • 在每一個數據庫中都有一個名爲 SYSTEM 的表空間,即系統表空間,該表空間是在創建數據庫或數據庫安裝時自動創建的,用於存儲系統的數據字典表、程序單元、過程、函數、包和觸發器等。

a. 表空間類型

  • 永久性表空間:一般保存表、視圖、過程和索引等的數據
  • 臨時性表空間:只用於保存系統中短期活動的數據
  • 撤銷表空間:用來幫助回退未提交的事務數據

b. 操作與運用

  • 創建表空間
【語法】
CREATE TABLESPACE 表空間名
 DATAFILE '數據文件路徑' SIZE 大小
 [AUTOEXTEND ON] [NEXT 大小]
 [MAXSIZE 大小];

【說明】
[]裏面內容可選項;數據文件路徑中若包含目錄需要先創建
SIZE 爲初始表空間大小,單位爲 K 或者 M
AUTOEXTEND ON 是否自動擴展
NEXT 爲文件滿了後擴展大小
MAXSIZE 爲文件最大大小,值爲數值或 UNLIMITED(表示不限大小)

【示例】
CREATE TABLESPACE regino_ts
 DATAFILE 'd:\Oracle_data\regino01.dbf' SIZE 10M
 AUTOEXTEND ON;
  • 查詢表空間
--管理員角色查看錶空間
SELECT file_name,tablespace_name,bytes,autoextensible 
FROM dba_data_files 
WHERE tablespace_name='regino_TS';
  • 修改表空間
【語法】
ALTER TABLESPACE 表空間名
 ADD DATAFILE '文件路徑' SIZE 大小
 [AUTOEXTEND ON] [NEXT 大小]
 [MAXSIZE 大小];

【示例】
ALTER TABLESPACE regino_ts
 ADD DATAFILE 'd:\Oracle_data\regino02.DBF' SIZE 5M
 AUTOEXTEND ON;
  • 刪除表空間
【語法】
DROP TABLESPACE 表空間名;
DROP TABLESPACE 表空間名 INCLUDING CONTENTS AND DATAFILES;

【說明】
第一個刪除語句只刪除表空間;第二個刪除語句則刪除表空間及數據文件

【示例】
DROP TABLESPACE regino_ts;
DROP TABLESPACE regino_ts INCLUDING CONTENTS AND DATAFILES; 

6. 數據庫用戶

a. 系統常見用戶

用戶 說明
sys 超級用戶,主要用來維護系統信息和管理實例,以 SYSDBA 或 SYSOPER 角色登錄。密 碼爲在安裝時設置的管理口令,如一般設置爲:orcl
system 默認的系統管理員,擁有 DBA 權限,通常用來管理 Oracle 數據庫的用戶、權限和存儲,以 Normal 方式登錄。密碼爲在安裝時設置的管理口令,如一般設置爲:orcl
scott 示範用戶,使用 users 表空間。一般該用戶默認密碼爲 tiger

b. 用戶管理

  • Oracle 中有個模式(schema)的概念,它是用戶的所有數據庫對象的集合;一般在創建用戶的同時會自動創建一個這樣的模式,名稱和用戶名稱一樣。模式(schema)是對用戶所創建的數據庫對象的總稱,在 Oracle 數據庫中任何數據庫對象都屬於一個特定用戶,一個用戶及其所擁有的對象即稱爲模式。模式對象包括表、視圖、索引、同義詞、序列、過程和程序包等。一個用戶與相同名稱的模式相關聯,所以又稱爲用戶模式。
  • 查詢系統用戶
select * from all_users;select * from dba_users; --更詳細的用戶信息 
  • 解鎖用戶
【語法】
ALTER USER 用戶名 ACCOUNT UNLOCK;

【示例】解鎖 hr 用戶
alter user hr account unlock;
  • 創建用戶
【語法】
CREATE USER 用戶名 IDENTIFIED BY 密碼
 DEFAULT TABLESPACE 表空間;

【示例】
CREATE USER regino IDENTIFIED BY regino
 DEFAULT TABLESPACE regino_ts
 TEMPORARY TABLESPACE temp;
  • 修改用戶密碼
【語法】
ALTER USER 用戶名 identified by 密碼

【示例】
ALTER USER regino identified by it;
  • 刪除用戶
【語法】
DROP USER 用戶名 CASCADE;

【示例】
DROP USER regino CASCADE; 

7. DCL 數據控制語言

a. 授予

【語法 1GRANT 角色權限(角色)[,角色權限] TO 用戶;

【示例 1--授予CONNECT和RESOURCE兩個角色
GRANT connect, resource TO regino;

【備註】使用如下語句可以查看 resource 角色下的權限
SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE='RESOURCE'

【語法 2GRANT 操作 ON 模式.對象 TO 用戶;

【示例 2--允許用戶查看、更新 EMP 表中的記錄
GRANT select,update ON SCOTT.emp TO regino;
--查看當前用戶的系統權限
select * from user_sys_privs;
--查看當前用戶的對象權限
select * from user_tab_privs;
--查看當前用戶的所有角色
select * from user_role_privs;

b. 撤銷

【語法 1REVOKE 角色權限(角色)[,角色權限] FROM 用戶;

【示例 1--撤銷CONNECT和RESOURCE兩個角色
REVOKE connect, resource FROM regino;

【語法 2REVOKE 操作 ON 模式.對象 FROM 用戶;

【示例 2--撤銷用戶查看、更新 EMP 表中的記錄的操作
REVOKE select,update ON SCOTT.emp FROM regino; 

8. DDL 數據定義語言

a. 創建表

【語法】
CREATE TABLE <table_name>( 
column1 DATATYPE [NOT NULL] [PRIMARY KEY], 
column2 DATATYPE [NOT NULL],
...
[constraint <約束名> 約束類型 (要約束的字段)
... ] );

【說明】
DATATYPE --是 Oracle 的數據類型
NUT NULL --可不可以允許資料有空的(尚未有資料填入)
PRIMARY KEY --是本表的主鍵
constraint --是對錶裏的字段添加約束.(約束類型有
 Check,Unique,Primary key,not null,Foreign key);

【示例】
create table t_student(
s_id number(8) PRIMARY KEY,
s_name varchar2(20) not null,
s_sex varchar2(8),
clsid number(8),
constraint u_1 unique(s_name),
constraint c_1 check (s_sex in ('MALE','FEMALE'))
);
--從現有的表創建表及複製其數據

【語法】
CREATE TABLE <table_name> as <SELECT 語句>

【示例】
create table emp as select * from scott.emp;
create table emp as select empno,ename from scott.emp --表結構只有
empno 和 ename 兩個字段及該兩字段對應的數據
--如果只複製表的結構不復製表的數據則:
create table emp as select * from scott.emp where 1=2;

b. 修改表

【語法 1】向表中添加新字段
ALTER TABLE <table_name> ADD (字段 1 類型 [NOT NULL],
字段 2 類型 [NOT NULL] ... );

【示例 1alter table t_student add (s_age number(3),s_address varchar2(20));

【語法 2】修改表中字段
ALTER TABLE <table_name> MODIFY(字段 1 類型,字段 2 類型 ... );

【示例 2alter table t_student modify(s_name varchar2(50),s_address 
varchar2(100));

【語法 3】刪除表中字段
ALTER TABLE <table_name> DROP(字段 1,字段 2... );

【示例 3alter table t_student drop(s_age,s_address);

【語法 4】修改表字段名稱
ALTER TABLE <table_name> RENAME COLUMN 原字段名稱 TO 新字段名稱;

【示例 4alter table t_student rename column s_id to s_no;

c. 刪除表

【語法 1--刪除表結構及數據(刪除後可在回收站查看並恢復)
DROP TABLE <table_name>;
--刪除表結構及數據(刪除後不可在回收站查看並恢復)
DROP TABLE <table_name> PURGE;

【示例 1drop table t_student;

d. 回收站

  • 查看回收站
--查看回收站
show recyclebin;select * from recyclebin;
  • 清空回收站
--清空回收站
purge recyclebin;

e. Oracle 數據類型

數據類型 描述
VARCHAR2(size) 可變長度的字符串,其最大長度爲 size 個字節;size 的最大值是 4000,而最小值是 1;你必須指定一個 VARCHAR2 的 size;
NVARCHAR2(size) 可變長度的字符串,依據所選的國家字符集,其最大長度爲 size 個字符或字節;size 的最大值取決於儲存每個字符所需的字節數,其上限爲 4000;你必須指定一個 NVARCHAR2 的 size;
NUMBER(p,s) 精度爲 p 並且數值範圍爲 s 的數值;精度 p 的範圍從 1 到 38;數值範圍 s 的範圍是從-84 到 127;例如:NUMBER(5,2) 表示整數部分最大 3 位,小數部分爲 2 位;NUMBER(5,-2) 表示數的整數部分最大爲 7 其中對整數的倒數 2 位爲 0,前面的取整。NUMBER 表示使用默認值,即等同於 NUMBER(5);
LONG 可變長度的字符數據,其長度可達 2G 個字節;
DATE 有效日期範圍從公元前 4712 年 1 月 1 日到公元后 9999 年 12 月 31 日
TIMESTAMP date 和 timestamp 都是對日期和時間的表示,只是兩種類型的精確度不同,前者精確到秒,後者精確到小數秒(fractional_seconds_precision),可以是 0 to 9,缺省是 6
RAW(size) 長度爲 size 字節的原始二進制數據,size 的最大值爲 2000 字節;你必須爲 RAW 指定一個 size;
LONG RAW 可變長度的原始二進制數據,其最長可達 2G 字節;
CHAR(size) 固定長度的字符數據,其長度爲 size 個字節;size 的最大值是 2000 字節,而最小值和默認值是 1;
NCHAR(size) 也是固定長度。根據 Unicode 標準定義
CLOB 一個字符大型對象,可容納單字節的字符;不支持寬度不等的字符集;最大爲 4G 字節
NCLOB 一個字符大型對象,可容納單字節的字符;不支持寬度不等的字符集;最大爲 4G 字節;儲存國家字符集
BLOB 一個二進制大型對象;最大 4G 字節
BFILE 包含一個大型二進制文件的定位器,其儲存在數據庫的外面;使得可以以字符流 I/O 訪問存在數據庫服務器上的外部 LOB;最大大小爲 4G 字節.

9. DML 數據操作語言

a. 新增

【語法 1INSERT INTO table_name (column1,column2,...) 
VALUES ( value1,value2, ...);

【示例 1insert into emp (empno,ename) values(1111,'regino');

【語法 2INSERT INTO <table_name> <SELECT 語句>;

【示例 2create table t1 as select * from emp where 1=2;
insert into t1 select * from emp where sal>2000;

b. 修改

【語法 1UPDATE table_name SET column1=new value,column2=new value,... 
WHERE <條件>;

【示例 1update emp set sal=3000 where ename='regino';

c. 查詢

i. 僞表 dual

  • DUAL 是一個虛擬表,用來構成 select 的語法規則,Oracle 保證 dual 裏面永遠只有一條記錄。以用它來做很多事情,如:
  • 查看當前用戶
select user from dual;
  • 用來調用系統函數
--查詢系統的當前時間並格式化
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;
  • 得到序列的下一個值或當前值
--獲得序列seq的下一個值
select seq.nextval from dual;
--獲得序列seq的當前值 
select seq.currval from dual;
  • 可以用做計算器
select 2*8 from dual;

ii. 僞列 rowid

  • rowid 是物理結構上的,在每條記錄 insert 到數據庫中時,都會有一個唯一的物理記錄,同一條記錄在不同查詢中對應的 rowid 相同。
【用法】
SELECT ROWID,字段名... FROM 表名; 

【示例】
select rowid, emp.* from emp;

iii. 僞列 rownum

  • rownum 是根據 sql 查詢出的結果給每行分配一個邏輯編號;每次的查詢都會有不同的編號。編號從 1 開始。
【用法】
SELECT ROWNUM,字段名... FROM 表名;

【注意】
ROWNUM 不能使用大於號“>” 即 select rownum, emp.* from emp where rownum > 2 是不對的,沒有任何
結果

【示例】
select rownum, emp.* from emp;
/* 關於分頁:由於不能使用>,所以爲了達到分頁目的得如下執行;如獲取第
2 頁數據(每頁 3 條)*/
select * from (select rownum r,emp.* from emp where rownum < 7) where
r > 3;
/* 關於排序:由於 rownum 是查詢結果的行編號,排序後這個編號便有可能被
打亂,如果需要該編號和排序的結果列表序號保持一致可以如下執行*/
select rownum,t.* from (select empno,ename from emp order by empno 
desc) t;

iv. 連接查詢

  • 準備查詢數據,將 scott 用戶下的 dept 表複製到 regino 用戶下。
使用 sys 用戶登錄系統;替 regino 用戶創建 dept 表,表結構和數據來自 scott.dept。
--執行語句如下
create table regino.dept as select * from scott.dept; 
  • 等值查詢
--查詢emp表中各用戶對應的部門名稱
select empno,ename,dname from emp,dept where
emp.deptno=dept.deptno;
--練習:按部門統計員工的人數,要求顯示部門號、部門名稱、和部門人數
select d.deptno,d.dname,count(e.empno) from dept d,emp e
where d.deptno=e.deptno 
group by d.deptno,d.dname; 
  • 左外/右外連接查詢:左外連接是在等號左邊的集合,無論條件是否成立均在結果集合,寫法就是在等號右邊使用(+),這個寫法是 Oracle 專用的,如果需要全數據庫類型通用應該使用 left join)
--按部門統計員工的人數,要求顯示部門號、部門名稱、和部門人數,部門
下沒有人的也將顯示
select d.deptno,d.dname,count(e.empno) from dept d,emp e 
where d.deptno=e.deptno(+) group by d.deptno,d.dname;
--上述語句的通用數據庫寫法(left join方式)
select d.deptno,d.dname,count(e.empno) from dept d left join emp e 
on d.deptno=e.deptno group by d.deptno,d.dname;
  • 自連接查詢:查詢的 2 張表是同一張表,一般是該表的字段之間存在上下級關係
--查詢員工和老闆的上下級關係
select e.ename || ' 的老闆是: '|| b.ename from emp e,emp b
where e.mgr=b.empno; 

【注意】上述查詢語句中的||表示爲字符的連接

v. 組合查詢

  • 計算部門工資總和,最高工資,最低工資
select deptno,sum(sal),max(sal),min(sal) from emp group by deptno;
  • 部門平均工資
--查詢部門的平均工資
select deptno,avg(sal) from emp group by deptno;
--查詢平均工資大於2000的部門,並按照平均工資降序排序
select deptno,avg(sal) 平均工資 from emp 
group by deptno
having avg(sal)>2000
order by 平均工資 desc ;
--查詢除了20部門以外,平均工資大於2000的部門
select deptno,avg(sal) from emp 
where deptno <> 20
group by deptno
having avg(sal)>2000; 

【注意】SQL 語句中的各子句執行順序:
from->where->group by->having->select->order by
  • 子查詢:將子查詢放入括號中;group by 後不能使用子查詢;select、from、where 後面都可以使用子查詢;可以將子查詢看作一張新表
--select後面的子查詢
select (select dname from dept where deptno=10),ename from emp
where deptno=10;
--from後面的子查詢
select * from (select ename,sal from emp);
--將子查詢視爲一個表
select e.ename,e.sal from (select ename,sal from emp) e;
--where後面的子查詢;查詢工資比10號部門員工中任意一個員工的工資低的員工信息
select * from emp where sal < (select min(sal) from emp where
deptno=10);
  • 其它查詢
--查詢姓名是5個字符的員工,且第二個字符是C,使用_只匹配一個字符並且不能標識0或多個字符
select * from emp where ename like '_C___';
--查詢員工姓名中含有‘_’的員工,使用\轉義字符
select * from emp where ename like '%\_%' escape '\';

d. 刪除

--根據條件刪除表數據
delete from emp where empno=0000
--清空表數據(表還在),不寫日誌,省資源,效率高,屬於數據定義語言
--先創建要清空數據的表
create table myemp as select * from emp;
--清空表數據
truncate table myemp;

10. TCL 事務控制語言

a. 提交

  • 事務的提交比較簡單;直接在執行 DML 語句後進行提交即可,如果不提交事務則剛剛通過 DML 語句進行修改的內容還未保存到數據庫中,只在當前用戶的連接會話中有效。要永久變更數據需要顯示地執行提交、回滾或者退出當前回話(如退出 sqlplus)。
  • 提交的命令爲:commit;

b. 保存點與回滾

  • 保存點 savepoint 一般與回滾 rollback 配合使用。在設置了 savepoint 後事務的粒度可以控制的更加細化,可以回滾到特定的保存點。
【語法】保存點 savepoint
SAVEPOINT <savepoint_name>;

【示例】 
--創建一個保存點,名稱爲 a
savepoint a;

【注意】當創建保存點之後執行的 DML 操作,可以進行回滾,而保存點之前未
提交的 DML 操作不受影響。

【語法】回滾
ROLLBACK [TO savepoint];

【示例】
--回滾到保存點 a,即在保存點 a 之後的所有未提交的 DML 都無效。 
rollback to a;

/*保存點與回滾完整示例*/
--1、創建保存點a
savepoint a;
--2、插入emp數據 it1
insert into emp(empno,ename) values(1234,'it1');
--3、創建保存點b
savepoint b;
--4、插入emp數據 it2
insert into emp(empno,ename) values(1235,'it2');
--5、查看emp表數據,存在it1、it2兩條數據
select ename from emp;
--6、回滾到保存點b,即it2數據將消失
rollback to b;
--7、查看emp表數據,存在it1的數據,it2已不在
select ename from emp;
--8、提交數據
commit;
--9、查看emp表數據,存在it1的數據
select ename from emp;
--10、回滾到保存點a,將報錯保存點不存在的錯誤信息
rollback to a;

11. 運算符

a. 算術運算符

  • +、-、*、/

b. 比較(關係)運算符

  • =、!=、<>、< 、 > 、 <= 、 >= 、 between…and… 、in 、like
    、is null

c. 邏輯運算符

  • AND(邏輯與),表示兩個條件必須同時滿足
  • OR(邏輯或),表示兩個條件中有一個條件滿足即可
  • NOT(邏輯非),返回與某條件相反的結果

d. 連接運算符

  • ||
【示例】
select '工號爲:' || empno || ' 的員工的姓名爲:'|| ename from emp;

e. 集合運算符

  • union(並集無重複)
  • union all(並集有重複)
  • intersect(交集,共有部分)
  • minus(減集,第一個查詢具有,第二個查詢不具有的數據)
【注意】:列數相關,對應列的數據類型兼容,不能含有 Long 類型的列,第一個 select 語句的列或別名作爲結果標題

--union(並集將去重複)
select * from emp where deptno=10
union
select * from emp where deptno=20;
--intersect(交集) 查詢工資即屬於1000~2000區間和1500~2500區間的工資
select ename,sal from emp where sal between 1000 and 2000
intersect
select ename,sal from emp where sal between 1500 and 2500;
--minus(減集)
select ename,sal from emp where sal between 1000 and 2000
minus
select ename,sal from emp where sal between 1500 and 2500;

f. 運算符優先級

優先級 運算符
1 算術運算符
2 連接符
3 比較符
4 IS[NOT]NULL, LIKE, [NOT]IN
5 [NOT] BETWEEN
6 NOT
7 AND
8 OR
  • 可以使用括號改變優先級順序;OR 的優先級最低,算術運算符的優先級最高。

12. 常用函數

a. 數值型函數

  • round(x[,y])
【功能】返回四捨五入後的值

【參數】x,y,數字型表達式,如果 y 不爲整數則截取 y 整數部分,如果 y>0 則四捨五入爲 y 位小數,如果 y 小於 0 則四捨五入到小數點向左第 y 位。

【返回】數字

【示例】
select round(5555.6666,2.1),round(5555.6666,-2.6),round(5555.6666)
from dual;

返回: 5555.67 , 5600 , 5556
  • trunc(x[,y])
【功能】返回 x 按精度 y 截取後的值

【參數】x,y,數字型表達式,如果 y 不爲整數則截取 y 整數部分,如果 y>0 則截取到 y 位小數,如果 y 小於 0 則截取到小數點向左第 y 位,小數前其它數據用 0 表示。

【返回】數字

【示例】
select trunc(5555.66666,2.1),
trunc(5555.66666,-2.6),trunc(5555.033333) from dual;

返回:5555.66 5500 5555

b. 字符型函數

  • LENGTH(c1)
【功能】返回字符串的長度;

【說明】多字節符(漢字、全角符等),按 1 個字符計算

【參數】C1 字符串

【返回】數值型

【示例】
select length('個人博客'),length('regino 個人博客') from dual;

LENGTH('個人博客') LENGTH('regino 個人博客')
------------------ ------------------------
 4 10
  • LPAD(c1,n[,c2])、RPAD(c1,n[,c2])
【功能】在字符串 c1 的左(右)邊用字符串 c2 填充,直到長度爲 n 時爲止

【說明】如果 c1 長度大於 n,則返回 c1 左邊 n 個字符

【參數】C1 字符串
 n 追加後字符總長度
 c2 追加字符串,默認爲空格

【返回】字符型

【示例】
select lpad('regino',10,'*'),rpad('regino',10,'*') from dual;
  • REPLACE(c1,c2[,c3])
【功能】將字符表達式值中,部分相同字符串,替換成新的字符串

【參數】
 c1 希望被替換的字符或變量
 c2 被替換的字符串
 c3 要替換的字符串,默認爲空(即刪除之意,不是空格)

【返回】字符型

【示例】
select replace('he love you','he','i') from dual;
  • SUBSTR(c1,n1[,n2])
【功能】取子字符串

【說明】多字節符(漢字、全角符等),按 1 個字符計算

【參數】在字符表達式 c1 裏,從 n1 開始取 n2 個字符;若不指定 n2,則從第 n1 個字符直到結束的字串.
【返回】字符型

【示例】
select substr('123456789',4,4),substr('123456789',3) from dual;

c. 日期函數

  • sysdate
【功能】:返回當前日期。
【參數】:沒有參數,沒有括號
【返回】:日期

【示例】select sysdate from dual;
  • add_months(d1,n1)
【功能】:返回在日期 d1 基礎上再加 n1 個月後新的日期。
【參數】:d1,日期型,n1 數字型
【返回】:日期

【示例】select sysdate,add_months(sysdate,3) from dual;
  • months_between(d1,d2)
【功能】:返回日期 d1 到日期 d2 之間的月數。
【參數】:d1,d2 日期型
【返回】:數字
如果 d1>d2,則返回正數
如果 d1<d2,則返回負數

【示例】
select sysdate,
months_between(sysdate,to_date('2015-01-01','YYYY-MM-DD'))2015元 旦,
months_between(sysdate,to_date('2016-01-01','YYYY-MM-DD'))2016元 旦 from dual;
  • extract(c1 from d1)
【功能】:日期/時間 d1 中,參數(c1)的值
【參數】:d1 日期型(date)/日期時間型(timestamp),c1 爲字符型(參數)
【參數表】:c1 對應的參數表詳見示例
【返回】:字符

【示例】
select
extract(YEAR from timestamp '2015-5-1 12:26:18 ' ),
extract(MONTH from timestamp '2015-5-1 12:26:18 ' ),
extract(DAY from timestamp '2015-1-5 12:26:18 ' ),
extract(hour from timestamp '2015-5-1 12:26:18 ' ) 小時,
extract(minute from timestamp '2015-5-1 12:26:18' ) 分鐘,
extract(second from timestamp '2015-5-1 12:26:18 ' )from dual;
select extract (YEAR from date '2015-5-1' ) from dual;
select sysdate 當前日期,
extract(YEAR from sysdate ),
extract(MONTH from sysdate ),
extract(DAY from sysdate )from dual;
--如下語句也可獲取年份、月份等
select to_number(to_char(sysdate,'yyyy')) from dual;

d. 轉換函數

  • TO_CHAR(x[[,c2],C3])
【功能】將日期或數據轉換爲 char 數據類型
【參數】
 x 是一個 date 或 number 數據類型。
 c2 爲格式參數
 c3 爲 NLS 設置參數
【返回】varchar2 字符型

【示例】
select to_char(sysdate,'YYYY-MM-DD HH24:MI:SS'FROM dual;
select to_char(1210.7, '$9,999.00'FROM dual;
  • TO_DATE(X[,c2[,c3]])
【功能】將字符串 X 轉化爲日期型
【參數】c2,c3,字符型,參照 to_char()
【返回】字符串
如果 x 格式爲日期型(date)格式時,則相同表達:date x
如果 x 格式爲日期時間型(timestamp)格式時,則相同表達:timestamp x

【示例】
select to_date('201212','yyyymm'),
to_date('2012.12.20','yyyy.mm.dd'),
(date '2012-12-20') XXdate,
to_date('2012-12-20 12:31:30','yyyy-mm-dd hh24:mi:ss'),
to_timestamp('2012-12-20 12:31:30','yyyy-mm-dd hh24:mi:ss'),
(timestamp '2012-12-20 12:31:30') XXtimestamp
from dual;
  • TO_NUMBER(X[[,c2],c3])
【功能】將字符串 X 轉化爲數字型
【參數】c2,c3,字符型
【返回】數字串

【示例】
select TO_NUMBER('201212') + 3,TO_NUMBER('450.05') + 1 from dual;
--等同上述結果
select '201212' + 3 from dual;

e. 聚合函數

  • sum:求和
  • avg:求平均數
  • count:計數
  • max:求最大值
  • min:求最小值

f. 分析函數

  • 分析函數中瞭解 rank()/dense_rank()/row_number() 的使用:
--查詢部門的員工工種情況,並在部門內重新進行排序;PARTITION BY類似
group by,根據ORDER BY排序字段的值重新由1開始排序。
--RANK 使用相同排序排名一樣,後繼數據空出排名;即有2個排序爲1的,那麼接下來的排序號則爲3
select deptno,ename,job,rank() over(partition by deptno order by
job) as myRank from emp e;
--DENSE_RANK使用,使用相同排序排名一樣,後繼數據不空出排名;即有2個排序爲1的,那麼接下來的排序號則爲2
select deptno,ename,job,dense_rank() over(partition by deptno order
by job) as myDenseRank from emp e;
--ROW_NUMBER使用,不管排名是否一樣,都按順序排名;即有2個排序爲1的,那麼排序號不會重現重複
select deptno,ename,job,row_number() over(partition by deptno order
by job) as myRowNumber from emp e;

g. 其它函數

  • NVL()/NVL2()
【語法】NVL (expr1, expr2)
【功能】若 expr1 爲 NULL,返回 expr2;expr1 不爲 NULL,返回 expr1。注意兩者的類型要一致

【示例】將員工的獎金如果是空的話則設置爲 0
select ename,sal,comm,nvl(comm,0) from emp;


【語法】NVL2 (expr1, expr2, expr3) 
【功能】expr1 不爲 NULL,返回 expr2;expr2 爲 NULL,返回 expr3。expr2 和 expr3 類型不同的話,expr3 會轉換爲 expr2 的類型

【示例】
select ename,job,nvl2(job,'job 有值','job 無值') from emp;
  • decode(條件,值 1,翻譯值 1,值 2,翻譯值 2,…值 n,翻譯值 n,缺省值)
【功能】根據條件返回相應值
【參數】c1, c2, ...,cn,字符型/數值型/日期型,必須類型相同或 null
注:值 1……n 不能爲條件表達式,這種情況只能用 case when then end 解決

含義解釋: 
 decode(條件,1,翻譯值 1,2,翻譯值 2,...值 n,翻譯值 n,缺省值) 
 該函數的含義如下: 
 IF 條件=1 THEN
 RETURN(翻譯值 1)
 ELSIF 條件=2 THEN
 RETURN(翻譯值 2)
 ......
 ELSIF 條件=值 n THEN
 RETURN(翻譯值 n) 
 ELSE
 RETURN(缺省值)
 END IF

【示例】根據員工的部門號,條件判斷找到對應的部門名稱
select ename,deptno,decode(deptno,10,'ACCOUNTING',20,'RESEARCH',30,'SALES'
,'無部門') from emp;

13. 視圖

a. 視圖簡介

  • 視圖是由一個或者多個表組成的虛擬表;那些用於產生視圖的表叫做該視圖的基表。視圖不佔用物理空間,這個也是相對概念,因爲視圖本身的定義語句還是要存儲在數據字典裏的。視圖只有邏輯定義。每次使用的時候只是重新執行 SQL。一個視圖也可以從另一個視圖中產生。視圖沒有存儲真正的數據,真正的數據還是存儲在基表中。一般出於對基本的安全性和常用的查詢語句會建立視圖;並一般情況下不對視圖進行新增、更新操作。
【語法】
--創建視圖
CREATE [OR REPLACE] VIEW <view_name> 
AS
<SELECT 語句>;

--刪除視圖
DROP VIEW <view_name> ;

b. 視圖操作

-- 授予regino用戶 創建視圖 的權限
grant create view to regino;

-- 登錄regino,創建視圖
create or replace view v_emp
as
select empno,ename from emp;

--通過視圖查詢數據
select * from v_emp;

--通過視圖添加數據,需要保證基表的其它數據項可以爲空
insert into v_emp(empno,ename) values(3333,'regino3');

--通過視圖修改數據
update v_emp set ename='個人博客3' where empno=3333;

--通過視圖刪除數據
delete from v_emp where empno=3333;

--基於多個基表的視圖,不建議使用視圖進行增刪改操作
create or replace view v_dept_emp
as
select dept.deptno,dept.dname,ename from emp inner join dept on emp.deptno=dept.deptno;

--查詢多個基表的視圖
select * from v_dept_emp;

--創建基於視圖的視圖
create or replace view vv_emp
as
select ename from v_emp;

--查詢基於視圖的視圖
select * from vv_emp;

--刪除視圖
drop view v_emp;
drop view v_dept_emp;
drop view vv_emp;

14. 同義詞

  • 同義詞是數據庫模式對象的一個別名,經常用於簡化對象訪問和提高對象訪問的安全性。在使用同義詞時,Oracle 數據庫將它翻譯成對應模式對象的名字。與視圖類似,同義詞並不佔用實際存儲空間,只有在數據字典中保存了同義詞的定義。在 Oracle 數據庫中的大部分數據庫對象,如表、視圖、同義詞、序列、存儲過程等,數據庫管理員都可以根據實際情況爲他們定義同義詞。隱藏對象名稱和所有者。

a. 私有同義詞

  • 私有 Oracle 同義詞由創建它的用戶所有;創建的用戶需要具有 CREATE SYNONYM 權限。
【語法】
CREATE SYNONYM <synonym_name> for <tablename/viewname...>

【示例】
--管理員 授權用戶regino創建同義詞的權限
grant create synonym to regino;
--創建私有同義詞
create synonym syn_emp for emp;
create synonym syn_v_emp for v_emp;--爲視圖v_emp創建私有同義詞(別
名)
--使用私有同義詞
select empno,ename from syn_emp;
update syn_emp set ename='regino5' where empno='1234';
--刪除同義詞
drop synonym syn_emp;

b. 公有同義詞

  • 公有 Oracle 同義詞由一個特殊的用戶組 Public 所擁有。顧名思義,數據庫中所有的用戶都可以使用公有同義詞。公有同義詞往往用來標示一些比較普通的數據庫對象,這些對象常需要引用。公有同義詞一般由管理員用戶創建及刪除,普通用戶需要創建及刪除需要 create public synonym 和 drop public synonym 權限。
【語法】
CREATE PUBLIC SYNONYM <synonym_name> for <tablename/viewname...>
--登陸sys管理員用戶,授權用戶regino創建、刪除(公有的刪除權限需要特別給定)公有同義詞權限
grant create public synonym,drop public synonym to regino;
--revoke create public synonym,drop public synonym from regino;

--登陸regino用戶創建公有同義詞 conn regino/regino;
create public synonym syn_public_emp for emp;

--使用公有同義詞
select * from syn_public_emp;

-- 登錄system管理員 conn system/orcl; 創建regino2並授權
--create user regino2 identified by regino2 default tablespace 
regino_ts;
--grant connect,resource to regino2;

--爲其它用戶regino2授權使用公有同義詞(需要給予使用表的權限)
grant select,update on regino.emp to regino2;
--revoke select,update on regino.emp from regino2;

--登陸regino2用戶下使用公有同義詞syn_public_emp
select * from syn_public_emp;
update syn_public_emp set ename='個人博客5' where empno=5555;

--刪除同義詞
--登陸regino,刪除公有同義詞
drop public synonym syn_public_emp;

15. 索引

  • 索引是建立在數據庫表中的某些列的上面,是與表關聯的,可提供快速訪問數據方式,但會影響增刪改的效率;常用類型(按邏輯分類):單列索引和組合索引、唯一索引和非唯一索引。
  • 什麼時候要創建索引
    (1)在經常需要搜索、主鍵、連接的列上
    (2)表很大,記錄內容分佈範圍很廣
    (3)在經常需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的
    (4)在經常使用在 WHERE 子句中的列上面創建索引
  • 什麼時候不要創建索引
    (1)表經常進行 INSERT/UPDATE/DELETE 操作
    (2)表很小(記錄超少)
    (3)列名不經常作爲連接條件或出現在 WHERE 子句中
    (4)對於那些定義爲 text, image 和 bit 數據類型的列不應該增加索引

a. 創建索引

【語法】
CREATE [UNIQUE] INDEX <index_name> ON <table_name>(字段 [ASC|DESC]);

【說明】
UNIQUE --確保所有的索引列中的值都是可以區分的。
[ASC|DESC] --在列上按指定排序創建索引。

(創建索引的準則:
1.如果表裏有幾百行記錄則可以對其創建索引(表裏的記錄行數越多索引的效
果就越明顯)2.不要試圖對錶創建兩個或三個以上的索引。
3.爲頻繁使用的行創建索引。) 

【示例】
--創建單列唯一索引,表中的列值將不允許重複
create unique index index_emp_empno on emp(empno);
--創建單列非唯一索引
create index index_emp_ename on emp(ename);
--創建組合列、唯一索引
create unique index index_emp_ename_job on emp(ename,job);
--創建組合列、非唯一索引
create index index_emp_job_sal on emp(job,sal);

b. 刪除索引

【語法】
DROP INDEX <index_name>;

【示例】
--刪除索引
drop index index_emp_empno;
drop index index_emp_ename;
drop index index_emp_ename_job;
drop index index_emp_job_sal;

16. 序列

  • 序列是 Oracle 提供的一個產生唯一數值型值的機制。通常用於表的主健值,序列只能保證唯一,不能保證連續。

a. 創建序列

【語法】
CREATE SEQUENCE <sequencen_name> 
[INCREMENT BY n]
[START WITH n]
[MAXVALUE n][MINVALUE n]
[CYCLE|NOCYCLE]
[CACHE n|NOCACHE];

INCREMENT BY n --表示序列每次增長的幅度;默認值爲 1.
START WITH n --表示序列開始時的序列號。默認值爲 1.
MAXVALUE n --表示序列可以生成的最大值(升序).
MINVALUE n --表示序列可以生成的最小值(降序).
CYCLE --表示序列到達最大值後,在重新開始生成序列.默認值爲 NOCYCLE。
CACHE n--允許更快的生成序列.預先生成 n 個序列值到內存(如果沒有使用
完,那下次序列的值從內存最大值之後開始;所以 n 不應該設置太大)

【示例】
--創建遞增序列
create sequence seq_test
increment by 1
start with 1
maxvalue 1000
nocycle;

--創建遞減序列
create sequence seq_test2
increment by -1
start with 5
maxvalue 5
minvalue 1
nocycle;

b. 序列使用

  • NEXTVAL 返回序列下一個值;第一次訪問時,返回序列的初始值,後繼每次調用時,按步長增加的值返回
【語法】
select <sequence_name>.nextval from dual;

【示例】
select seq_test.nextval from dual; 
  • CURRVAL 返回序列的當前值.注意在剛建立序列後,序列的 CURRVAL 值爲 NULL,所以不能直接使用。使用過 NEXTVAL 訪問序列後才能使用
【語法】查看序列的當前值
select <sequence_name>.currval from dual;

【示例】
select seq_test.nextval from dual;
select seq_test.currval from dual;
  • 運用序列
-- 創建序列
create sequence seq_emp_empno
start with 1000
increment by 1
maxvalue 9000
minvalue 1000
nocycle;

-- 使用序列作爲主鍵插入emp表的empno列
insert into emp(empno,ename)
values(seq_emp_empno.nextval,'regino1');
insert into emp(empno,ename)
values(seq_emp_empno.nextval,'regino2');

-- 查看emp表數據
select empno,ename from emp;

-- 查看當前序列的值
select seq_emp_empno.currval from dual;

--修改序列
alter sequence seq_emp_empno
maxvalue 9999
cycle;

c. 刪除序列

【語法】
DROP SEQUENCE <sequence_name> 

【示例】
drop sequence seq_test;

d. 序列與 sys_guid

  • sys_guid 和序列都可以作爲主鍵值。
--使用SYS_GUID函數,32位,由時間戳和機器標識符生成,保證唯一
select sys_guid() from dual;

17. 分區表

a. 分區表用途

  • 分區表通過對分區列的判斷,把分區列不同的記錄,放到不同的分區中。分區完全對應用透明。Oracle 的分區表可以包括多個分區,每個分區都是一個獨立的段(SEGMENT),可以存放到不同的表空間中。查詢時可以通過查詢表來訪問各個分區中的數據,也可以通過在查詢時直接指定分區的方法來進行查詢。
  • 分區表的優點:
    (1)由於將數據分散到各個分區中,減少了數據損壞的可能性;
    (2)可以對單獨的分區進行備份和恢復;
    (3)可以將分區映射到不同的物理磁盤上,來分散 IO;
    (4)提高可管理性、可用性和性能。
  • 數據量大的表,一般大於 2GB;數據有明顯的界限劃分;對於 Long 和 Long Raw 類型列不能使用分區。

b. 分區表類型

  • 一般包括範圍分區,散列分區,列表分區、複合分區(範圍-散列分區,範圍-列表分區)、間隔分區和系統分區等。

i. 範圍分區

  • 範圍分區根據數據庫表中某一字段的值的範圍來劃分分區。
【語法】
在 Create Table 語句後增加
PARTITION BY RANGE(column_name)(
	PARTITION part1 VALUES LESS THAN (range1) [TABLESPACE tbs1],
	PARTITION part2 VALUES LESS THAN (range2) [TABLESPACE tbs2],
	...
	PARTITION partN VALUES LESS THAN (MAXVALUE) [TABLESPACE tbsN]
);

【說明】
MAXVALUE:當分區列值都不在設置的範圍內時,新增數據將到這個分區中

【示例】
-- 創建表,並設置分區
create table myemp(
	empno number(4) primary key,
	ename varchar2(10),
	hiredate date,
	sal number(7,2),
	deptno number(2)
)
partition by range(sal) (
	partition p1 values less than(1000),
	partition p2 values less than(2000),
	partition p3 values less than(maxvalue)
);

-- 插入數據
insert into myemp(empno,ename,hiredate,sal,deptno)
select empno,ename,hiredate,sal,deptno from emp;

-- 查看工資1000-2000的數據
select * from myemp partition(p2);

-- 刪除工資小於1000的數據
delete from myemp partition(p1);

-- 查看數據
select * from myemp;

ii. 列表分區

  • 列表分區明確指定了根據某字段的某個具體值進行分區,而不是像範圍分區那樣根據字段的值範圍來劃分的。
【語法】
在 Create Table 語句後增加
PARTITION BY LIST(column_name)(
	PARTITION part1 VALUES (values_list1),
	PARTITION part2 VALUES (values_list2),
	...
	PARTITION partN VALUES (DEFAULT)
);

其中:
	column_name 是以其爲基礎創建列表分區的列。
	part1...partN 是分區的名稱。
	values_list 是對應分區的分區鍵值的列表。
	DEFAULT 關鍵字允許存儲前面的分區不能存儲的記錄。

【示例】
-- 創建表,並設置分區
create table myemp2(
	empno number(4) primary key,
	ename varchar2(10),
	hiredate date,
	sal number(7,2),
	deptno number(2)
)
partition by list(deptno) (
	partition dept10 values(10),
	partition dept20 values(20),
	partition dept30 values(30),
	partition deptx values(default)
);

-- 插入數據
insert into myemp2(empno,ename,hiredate,sal,deptno)
select empno,ename,hiredate,sal,deptno from emp;

-- 查看部門20的數據
select * from myemp2 partition(dept20);

-- 刪除部門30的數據
delete from myemp2 partition(dept30);

-- 查看數據
select * from myemp2;

18. PL/SQL

  • pl/sql:塊結構語言,是 sql(Structured Query Language)語言的一種擴展,結合了 Oracle 過程語言(procedural language)進行使用。
  • pl/sql 塊由三部分構成:聲明部分、執行部分、異常部分。
  • PL/SQL 結構
[DECLARE]
 --聲明變量等;
BEGIN
 --程序主要部分,一般用來執行過程語句或 SQL 語句;
[EXCEPTION]
--異常處理;
END;

a. 運算符

在這裏插入圖片描述

b. 變量與常量

  • 數據類型:
    • 常用標準類型:CHAR(CHARATER,NCHAR),VARCHAR2,NUMBER(P,S),DATE,BOOLEAN 等。
    • 屬性類型:%TYPE 與 %ROWTYPE
      • %TYPE:可以用來定義數據變量的類型與已定義的數據變量(表中的列)一致。
      • %ROWTYPE:與某一數據庫表的結構一致(修改數據庫表結構,可以實時保持一致);
      • 訪問方式聲明爲 rowtype 的 變量名.字段名。

i. 基本類型

  • 聲明
【變量聲明】
<變量名> 類型[:=初始值];
【示例】
name varchar2(20) := 'regino';

【常量聲明】
<變量名> CONSTANT 類型:=初始值;
【示例】
pi constant number(5,3):=3.14;
  • 運用
/*定義常量或變量、賦值使用示例*/
DECLARE
	p_empno constant number(4):=7369;
	p_ename varchar2(10);
	p_sal number(7,2);
	p_comm number(7,2);
BEGIN
	--賦值方式一:使用select into給變量賦值
	select ename,sal into p_ename,p_sal from emp where empno 
=p_empno;
 
	--賦值方式二:使用賦值操作符“:=”給變量賦值
	p_comm:=500;
 
	--輸出相關信息,DBMS_OUTPUT.PUT_LINE爲具有輸出功能的函數
	dbms_output.put_line('員工號:'|| p_empno||',姓名:'||
p_ename||',工資:'|| p_sal||',獎金:'|| p_comm);
END;

【注意】
dbms_output 是 Oracle 提供的輸出對象
put_line 是其一個方法,用於輸出一個字符串
new_line 是其一個方法,用於輸出新的一行(換行)

ii. %type 類型

  • 聲明
【聲明】
變量名稱 表名.字段%type;
【示例:】
--表示變量 name 的類型和 emp.ename 的類型相同
name emp.ename%type;
  • 運用
/*定義常量或變量、賦值使用示例*/
DECLARE
	p_empno constant number(4):=7369;
	p_ename emp.ename%type;
	p_sal emp.sal%type;
	p_comm emp.comm%type;
BEGIN
	--賦值方式一:使用select into給變量賦值
	select ename,sal into p_ename,p_sal from emp where empno =
p_empno;

	--賦值方式二:使用賦值操作符“:=”給變量賦值
	p_comm:=500;
 
	--輸出相關信息,DBMS_OUTPUT.PUT_LINE爲具有輸出功能的函數
	dbms_output.put_line('員工號:'|| p_empno||',姓名:'||
p_ename||',工資:'|| p_sal||',獎金:'|| p_comm);
END;

iii. %rowtype 類型

  • 聲明
【聲明】
變量名稱 表%rowtype;
【示例:】
--表示變量 test 的類型爲 emp 表的行類型;也有 .empno; .ename; .sal ;等屬性
test emp%rowtype;
  • 運用
/*定義常量或變量、賦值使用示例*/
DECLARE
	p_empno constant number(4):=7369;
	emp_info emp%rowtype;
	p_comm emp.comm%type;
BEGIN
	--賦值方式一:使用select into給變量賦值
	select * into emp_info from emp where empno = p_empno;

	--賦值方式二:使用賦值操作符“:=”給變量賦值
	p_comm:=500;
 
	--輸出相關信息,DBMS_OUTPUT.PUT_LINE爲具有輸出功能的函數
	dbms_output.put_line('員工號:'|| p_empno||',姓名:'||
emp_info.ename ||',工資:'|| emp_info.sal ||',獎金:'|| p_comm);
END;

c. 控制語句

i. 條件語句

【語法】
IF <條件 1> THEN
	語句
[ELSIF <條件 2> THEN
	語句]
 .
 .
 .
[ELSIF <條件 n> THEN
	語句]
[ELSE 
	語句]
END IF;

【示例】
/*
根據員工的工資判斷其工資等級(工資大於等於 5000 爲 A 級,工資大於等於
4000 爲 B 級,工資大於等於 3000 爲 C 級,工資大於等於 2000 爲 D 級,其它
爲 E 級)
*/
DECLARE
	p_empno number(4):=7566;
	p_sal emp.sal%type;
BEGIN 
	--用變量代替條件語句中的真值
	select sal into p_sal from emp where empno = p_empno;
	IF p_sal >= 5000 THEN
	dbms_output.put_line('員工號爲:' || p_empno || '的員工的工
資級別爲:A級');
	ELSIF p_sal >= 4000 THEN
	dbms_output.put_line('員工號爲:' || p_empno || '的員工的工
資級別爲:B級');
	ELSIF p_sal >= 3000 THEN
	dbms_output.put_line('員工號爲:' || p_empno || '的員工的工
資級別爲:C級');
	ELSIF p_sal >= 2000 THEN
	dbms_output.put_line('員工號爲:' || p_empno || '的員工的工
資級別爲:D級');
	ELSE
	dbms_output.put_line('員工號爲:' || p_empno || '的員工的工
資級別爲:E級');
	END IF; 
END;

ii. 循環語句

  • LOOP
LOOP
	語句;
	EXIT WHEN <條件>
END LOOP;

【示例】
/*
計算 1-10 的總和
*/
DECLARE
	p_sum number(4):=0;
	p_num number(2):=1;
BEGIN 
	LOOP
	p_sum := p_sum + p_num;
	p_num := p_num + 1;
	EXIT WHEN p_num > 10;
	END LOOP;
	dbms_output.put_line('1-10的總和爲:' || p_sum);
END;
  • WHILE LOOP
WHILE <條件>
LOOP
	語句;
END LOOP;

【示例】
/*
計算 1-10 的總和
*/
DECLARE
	p_sum number(4):=0;
	p_num number(2):=1;
BEGIN 
	WHILE p_num <= 10
	LOOP
	p_sum := p_sum + p_num;
	p_num := p_num + 1;
	END LOOP;
	dbms_output.put_line('1-10的總和爲:' || p_sum);
END; 
  • FOR
FOR <循環變量> IN [REVERSE] 下限..上限
LOOP
	語句;
END LOOP;

【說明】..兩點表示範圍,1..4 表示時將從 14 進行循環,起始(例如 1)寫前邊,REVERSE 表示反轉,循環時變成從 41 進行。

【示例】
/*
計算 1-10 的總和
*/
DECLARE
	p_sum number(4):=0;
	p_num number(2):=1;
BEGIN 
	FOR p_num IN 1..10
	LOOP
	p_sum := p_sum + p_num;
	END LOOP;
	dbms_output.put_line('1-10的總和爲:' || p_sum);
END;

iii. 順序語句

  • 指定順序執行的語句;主要包括 null 語句。null 語句:是一個可執行語句,相當於一個佔位符或不執行操作的空語句。主要用來提高程序語句的完整性和程序的可讀性。
/*
輸出 1-10 的數字但跳過數字 4
*/
DECLARE
	flag number(2):=0;
BEGIN 
	WHILE flag < 10 
	LOOP
	flag := flag + 1;
	if flag = 4 then
	null;-- 佔位,不能去掉
	else
	dbms_output.put_line(flag);
	end if;
	END LOOP; 
END;

d. 異常處理

i. 異常語法

EXCEPTION 
	WHEN <異常類型> THEN
		語句;
	WHEN OTHERS THEN
		語句;
  • 常配套使用的函數:
    • SQLCODE 函數:返回錯誤代碼,
    • SQLERRM 函數:返回錯誤信息
  • 例如輸出異常信息:DBMS_OUTPUT.PUT_LINE(‘其它異常,代碼號:’||SQLCODE||’,
  • 異常描述:’||SQLERRM);

ii. 預定義異常

  • 預定義異常指 PL/SQL 程序違反 Oracle 規則或超越系統限制時隱式引發
    (由 Oracle 自動引發)。
  • 常見的預定義異常
    • CURSOR_ALREADY_OPEN 試圖"OPEN"一個已經打開的遊標
    • DUP_VAL_ON_INDEX 試圖向有"UNIQUE"中插入重複的值
    • INVALID_CURSOR 試圖對以關閉的遊標進行操作
    • INVALID_NUMBER 在 SQL 語句中將字符轉換成數字失敗
    • LOGIN_DENIED 使用無效用戶登陸
    • NO_DATA_FOUND 沒有找到數據時
    • NOT_LOGIN_ON 沒有登陸 Oracle 就發出命令時
    • PROGRAM_ERROR PL/SQL 存在諸如某個函數沒有"RETURN"語句等內部問題
    • STORAGE_ERROR PL/SQL 耗盡內存或內存嚴重不足
    • TIMEOUT_ON_RESOURCE Oracle 等待資源期間發生超時
    • TOO_MANY_ROWS "SELECT INTO"返回多行時
    • VALUE_ERROR 當出現賦值錯誤
    • ZERO_DIVIDE 除數爲零
【示例】
/*
預定義異常捕獲並處理
*/
DECLARE
	p_result number(2);
BEGIN 
	p_result := 1/0;
	dbms_output.put_line('沒有異常!');
	EXCEPTION
	WHEN ZERO_DIVIDE THEN
		dbms_output.put_line('除數不能爲0!代碼爲:'|| sqlcode ||',異常信息爲:' || sqlerrm);
	WHEN OTHERS THEN
		dbms_output.put_line('其它異常!代碼爲:'|| sqlcode || ',異常信息爲:' || sqlerrm);
END;

iii. 自定義異常

  • 自定義異常:程序在運行過程中,根據業務等情況,認爲非正常情況,可以自定義異常。對於這種異常,主要分三步來處理:
    1. 定義相關異常;在聲明部分定義相關異常,
      格式:<自定義異常名稱> EXCEPTION;
    2. 拋出異常;在出現異常部分拋出異常,
      格式:RAISE <異常名稱>;
    3. 處理異常;在異常處理部分對異常進行處理,
      格式:when <自定義異常名稱> then …,
  • 處理異常也可以使用 RAISE_APPLICATION_ERROR(ERROR_NUMBER,ERROR_MESSAGE) 存儲過程進行處理,其中參數 ERROR_NUMBER 取值爲 -20999~-20000 的負整數,參數 ERROR_MESSAGE 爲異常文本消息。
【示例】
/*
判斷 emp 中相應 empno 對應用戶的獎金是否低於 500,如果低於則拋出並處理
自定義異常
*/
DECLARE
	p_comm emp.comm%type;
	--自定義異常,名稱爲comm_exception
	comm_exception EXCEPTION;
BEGIN 
	select nvl(comm,0) into p_comm from emp where empno=7499;
	if p_comm >= 500 then
		dbms_output.put_line('獎金大於等於500。');
	else
		RAISE comm_exception;
	end if;
	EXCEPTION
	WHEN comm_exception THEN
		RAISE_APPLICATION_ERROR(-20001,'獎金低於500,太少了!');
		--dbms_output.put_line('獎金低於500!');
	WHEN OTHERS THEN
		dbms_output.put_line('其它異常!代碼爲:'|| sqlcode || ',異常信息爲:' || sqlerrm);
END;

19. 遊標

a. 顯式遊標

  • 遊標是映射在結果集中一行數據上的位置實體,使用遊標,便可以訪問結果集中的任意一行數據了,將遊標放置到某行後,即可對該行數據進行操作;從上向下依次迭代結果集。

i. 遊標語法

【定義語法】
CURSOR <遊標名> IS <SELECT 語句> ;
【操作】
	OPEN <遊標名> --打開遊標
	FETCH <遊標名> INTO 變量 1,變量 2,變量 3,....變量 n,;
	或者
	FETCH <遊標名> INTO 行對象; --取出遊標當前位置的值
	CLOSE <遊標名> --關閉遊標
【屬性】
	%NOTFOUND --如果 FETCH 語句失敗,則該屬性爲"TRUE",否則爲"FALSE";
	%FOUND --如果 FETCH 語句成功,則該屬性爲"TRUE",否則爲"FALSE";
	%ROWCOUNT --返回遊標當前行的行數;
	%ISOPEN --如果遊標是開的則返回"TRUE",否則爲"FALSE";

ii. 遊標使用

  • 使用遊標顯示員工表中所有的員工姓名、工作和工資
declare
	cursor cur_emp is select ename,job,sal from emp;
	p_ename emp.ename%type;
	p_job emp.job%type;
	p_sal emp.sal%type;
begin
	--打開遊標
	open cur_emp;
	loop
		--取遊標數據,從上往下移動一行
		fetch cur_emp into p_ename, p_job, p_sal;
		--如果下移後沒有數據,則退出
		exit when cur_emp%notfound;
		--如果存在數據,則處理
		dbms_output.put_line('姓名爲:' || p_ename || ',工作爲:'|| p_job || ',工資爲:' || p_sal);
	end loop;
	--關閉遊標
	close cur_emp;
end; 
  • 使用遊標顯示指定部門下的所有的員工姓名、工作和工資
代參數的遊標 
【定義】
CURSOR <遊標名>(參數列表) IS <SELECT 語句>;

【示例】
declare
	cursor cur_emp(dno emp.deptno%type) is select ename,job,sal from emp where deptno=dno;
	r_cur_emp cur_emp%rowtype;
begin
	--打開遊標
	open cur_emp(20);
	loop
		--取遊標數據,從上往下移動一行
		fetch cur_emp into r_cur_emp;
		--如果下移後沒有數據,則退出
		exit when cur_emp%notfound;
		--如果存在數據,則處理
		dbms_output.put_line('姓名爲:' || r_cur_emp.ename || ',工
作爲:' || r_cur_emp.job || ',工資爲:' || r_cur_emp.sal);
	end loop;
	--關閉遊標
	close cur_emp;
end;

--參考:使用 while 循環實現
declare
	cursor cur_dept_emps(dno emp.deptno%type) is select ename,job,sal  from emp where deptno=dno;
	emp_info cur_dept_emps%rowtype;
begin
	open cur_dept_emps(20);
	fetch cur_dept_emps into emp_info;
	while cur_dept_emps%found
	loop
		dbms_output.put_line('員工姓名爲:'||emp_info.ename||',工作爲:'||emp_info.job||',工資爲:'||emp_info.sal);
		fetch cur_dept_emps into emp_info;
	end loop;
	close cur_dept_emps;
end;

--參考:使用 for 循環實現
declare
	cursor cur_dept_emps(dno emp.deptno%type) is select ename,job,sal from emp where deptno=dno;
	emp_info cur_dept_emps%rowtype;
begin
	for emp_info in cur_dept_emps(20)
	loop
		if cur_dept_emps%found then
			dbms_output.put_line('員工姓名爲:'||emp_info.ename||',工作爲:'||emp_info.job||',工資爲:'||emp_info.sal);
		end if;
	end loop;
end; 
  • 使用遊標按員工的工種漲工資,總裁 800,經理 600,其他人員 300
declare
	cursor cur_emp is select empno,job from emp;
	p_empno emp.empno%type;
	p_job emp.job%type;
begin
	--打開遊標
	open cur_emp;
	loop
		--取遊標數據,從上往下移動一行
		fetch cur_emp into p_empno, p_job;
		--如果下移後沒有數據,則退出
		exit when cur_emp%notfound;
		--如果存在數據,則處理
		if 'PRESIDENT'= p_job then 
			update emp set sal = sal + 800 where empno = p_empno;
		elsif 'MANAGER' = p_job then
			update emp set sal = sal + 600 where empno = p_empno;
		else
			update emp set sal = sal + 300 where empno = p_empno;
		end if;
	end loop;
	--關閉遊標
	close cur_emp;
	--提交修改
	commit;
end;

b. 隱式遊標

  • 當執行一個 SQL 語句時,Oracle 會自動創建一個隱式遊標,隱式遊標主要處理 DML 語句,該遊標的名稱是 sql。隱試遊標不能進行"OPEN",“CLOSE”,“FETCH” 這些操作。
  • 屬性:
    • %NOTFOUND --如果 DML 語句沒有影響到任何一行時,則該屬性爲"TRUE",否則爲"FALSE";
    • %FOUND --如果 DML 語句影響到一行或一行以上時,則該屬性爲"TRUE",否則爲"FALSE";
    • %ROWCOUNT --返回遊標當最後一行的行數;
【示例】
/*
通過更新語句判斷隱式遊標的存在
*/
begin
	update emp set comm=comm + 300 where empno = 7369;
	if sql%notfound then
		dbms_output.put_line('empno對應的員工不存在');
	else
		dbms_output.put_line('empno對應的員工數爲:' ||
		sql%rowcount);
	end if;
end;

20. 存儲過程與存儲函數

a. 存儲過程

  • 存儲過程是命名的 pl/sql 程序塊,封裝數據業務操作,具有模塊化、可重
    用、可維護、更安全特點;並且可以被程序調用。一般有 4 類型的存儲過程,分別爲不帶參數、帶輸入參數、帶輸出參數、帶輸入輸出參數。

i. 語法

【語法】
	CREATE [OR REPLACE] PROCEDURE <過程名>[(參數列表)] IS|AS
	[局部變量聲明]
	BEGIN
		可執行語句
	[EXCEPTION
		異常處理語句]
	END [<過程名>];

OR REPLACE:如果系統已存在該存儲過程,將被替換
參數列表:參數不需要聲明長度,可選
參數變量的類型:in 爲默認類型,表示輸入; out 表示只輸出;in out 表示即
輸入又輸出;

【調用方式】 
在 PL/SQL 塊中直接使用過程名; 
在 PL/SQL 程序外使用 exec[ute] <過程名>[(參數列表)];

ii. 無參存儲過程

-- 授予regino創建存儲過程的權限
grant create procedure to regino;

/*
使用無參存儲過程,注意無參存儲過程創建時不能使用()
*/
create or replace procedure pro_helloWorld
as
begin
	dbms_output.put_line('Hello World.');
end;

-- 方式一:調用存儲過程,可加可不加()
begin
	pro_helloWorld;
end;

-- 方式二:調用存儲過程,可加可不加()
exec pro_helloWorld;

iii. 有輸入參數存儲過程

/*
使用有輸入參存儲過程
*/
create or replace procedure pro_add_emp(
	p_empno in emp.empno%type,
	p_ename in varchar2,
	p_sal number
)
as

--將輸入參數對應的數據插入emp表
begin
	insert into emp(empno, ename,sal) values(p_empno, p_ename, p_sal);
end; /

-- 調用存儲過程,向emp表插入新數據
begin
	pro_add_emp(2001,'regino2001',3000);
	pro_add_emp(2002,'regino2002',2000);
	pro_add_emp(2003,'regino2003',4000);
end;

iv. 有輸出參數存儲過程

/*
使用有輸出參存儲過程,計算 1 到 10 的總和並通過參數返回
*/
create or replace procedure pro_1to10_sum(
	p_sum out number
)
as
tem_sum number(4):=0;
begin
	for i in 1..10
	loop
		tem_sum := tem_sum + i;
	end loop;
	p_sum := tem_sum;
end; /

-- 調用存儲過程
declare
	p_sum number(4);
begin
	pro_1to10_sum(p_sum);
	dbms_output.put_line('1至10的和爲:'|| p_sum);
end;

v. 有輸入輸出參數存儲過程

/*
使用有輸入、輸出參存儲過程;根據 empno 查詢該員工號對應的員工的姓名和
工資
*/
create or replace procedure pro_query_enameAndSal_by_empno(
	s_empno emp.empno%type,
	s_ename out emp.ename%type,
	s_sal out emp.sal%type
)
as
begin
	select ename,sal into s_ename, s_sal from emp where empno=
	s_empno;
end; /

-- 調用存儲過程
declare
	p_ename emp.ename%type;
	p_sal emp.sal%type;
begin
	--pro_query_enameAndSal_by_empno(7369, p_ename, p_sal);
	pro_query_enameAndSal_by_empno(7369, s_sal => p_sal, s_ename => p_ename);
	dbms_output.put_line('員工號爲7369的員工名稱爲:'|| p_ename||',其
工資爲:'|| p_sal);
end;

vi. 程序中調用存儲過程

package cn.regino;

import Java.sql.CallableStatement;
import Java.sql.Connection;
import Java.sql.DriverManager;
import Java.sql.SQLException;
import Oracle.jdbc.OracleTypes;

public class TestProcedure {
	public static void main(String[] args) {
		Connection conn = null;
		CallableStatement call = null;
		try {
			Class.forName("Oracle.jdbc.OracleDriver");
			String url = "jdbc:Oracle:thin:@localhost:1521:orcl";
			conn = DriverManager.getConnection(url, "regino", "regino");
			call = conn.prepareCall("{call pro_query_enameAndSal_by_empno(?,?,?)}");
			//設置輸入型參數
			call.setInt(1, 7369);
			//註冊輸出型參數
			call.registerOutParameter(2, OracleTypes.VARCHAR);
			call.registerOutParameter(3, OracleTypes.NUMBER);
			//調用存儲過程
			call.execute();
			//獲取返回值
			String ename = call.getString(2);//員工名稱
			double sal = call.getDouble(3);//員工工資
			System.out.println("員工號爲7369的員工名稱爲:" + 
			ename + ",工資爲:" + sal);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(call != null){
					call.close();
				}
				if(conn != null){
					conn.close();
				}
			} catch (SQLException e) {
			e.printStackTrace();
			} 
		} 
	} 
}

vii. 刪除存儲過程

【語法】
DROP PROCEDURE <過程名>;

【示例】
drop procedure pro_1to10_sum;

b. 存儲函數

  • 存儲函數與過程不同的是,存儲函數有 return 語句;一般情況下如果在需要一個返回值時可使用存儲函數。

i. 語法

CREATE [OR REPLACE] FUNCTION <函數名>[(參數列表)] RETURN 數據類型
IS|AS
	[局部變量聲明]
	BEGIN
		可執行語句
		[EXCEPTION 異常處理語句]
		RETURN 返回值;
	END [<函數名>];

變量的類型:in 爲默認類型,表示輸入; out 表示只輸出;in out 表示即輸入
又輸出;

【使用方式】
直接在 select 中使用和其它系統函數使用方式一樣; 在 PL/SQL 塊中調用使用;

ii. 無參存儲函數

/*
使用無參存儲函數;注意創建時函數名稱不能使用()
但是在調用時候可加可不加()
*/
create or replace function fun_helloWorld
return varchar2
	as
begin
	return 'Hello World';
end; 

-- 方式1:調用存儲函數
select fun_helloWorld() from dual;

-- 方式2:調用存儲函數
declare
	str varchar2(20);
begin
	str :=fun_helloWorld;
	dbms_output.put_line(str);
end;

iii. 有輸入參數存儲函數

/*
使用存儲函數:根據員工號,查詢並返回該員工的年薪
*/
create or replace function fun_get_annualSal_by_empno(p_empno  emp.empno%type)
return number as
	p_sal emp.sal%type;
	p_comm emp.comm%type;
begin
	select sal,comm into p_sal, p_comm from emp where empno=p_empno;
	return 12*p_sal + nvl(p_comm,0);
end; 

-- 調用存儲函數
select fun_get_annualSal_by_empno(7369) from dual;

iv. 有輸入輸出參數存儲函數

/*
使用具有輸入輸出參數的存儲函數:根據員工號,查詢並返回該員工的年薪,姓名,獎金
*/
create or replace function fun_get_annualSal_by_empno2(
	p_empno emp.empno%type,
	p_ename out emp.ename%type,
	p_comm out emp.comm%type
)
return number as
	p_sal emp.sal%type;
begin
	select ename,sal,nvl(comm,0) into p_ename,p_sal, p_comm from emp 
	where empno=p_empno;
	return 12*p_sal + p_comm;
end; /

-- 調用存儲函數
declare
	p_annualSal number(10,2);
	p_ename emp.ename%type;
	p_comm emp.comm%type;
begin
	p_annualSal := fun_get_annualSal_by_empno2(7499,p_ename,p_comm);
	dbms_output.put_line('員工姓名爲:'||p_ename||',獎金爲:'||p_comm||',年薪爲:'||p_annualSal);
end;

v. 程序中調用存儲函數

package cn.regino;

import Java.sql.CallableStatement;
import Java.sql.Connection;
import Java.sql.DriverManager;
import Java.sql.SQLException;
import Oracle.jdbc.OracleTypes;

public class TestFunction {
	public static void main(String[] args) {
		Connection conn = null;
		CallableStatement call = null;
		try {
			Class.forName("Oracle.jdbc.OracleDriver");
			String url = "jdbc:Oracle:thin:@localhost:1521:orcl";
			conn = DriverManager.getConnection(url, "regino", "regino");
			call = conn.prepareCall("{? = call  fun_get_annualSal_by_empno2(?,?,?)}");
			//註冊存儲函數返回值
			call.registerOutParameter(1, OracleTypes.DOUBLE);
			//設置輸入參數,員工號
			call.setInt(2, 7499);
			//註冊輸出參數,員工姓名
			call.registerOutParameter(3, OracleTypes.VARCHAR);
			//註冊輸出參數,獎金
			call.registerOutParameter(4, OracleTypes.DOUBLE);
			call.execute();
			System.out.println("員工姓名爲:" + 
			call.getString(3) + ",獎金爲:" + call.getDouble(4) + ",年薪爲:" + call.getDouble(1));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(call != null){
				call.close();
				}
				if(conn != null){
				conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

vi. 刪除存儲函數

【語法】
DROP FUNCTION <函數名>;

【示例】
drop function fun_helloWorld;
drop function fun_get_annualSal_by_empno;
drop function fun_get_annualSal_by_empno2;

c. 存儲過程與存儲函數的區別

  • 返回值的區別,函數一定要有 1 個返回值或有多個通過輸出參數的返回值,而存儲過程是通過輸出參數返回的,可以有多個或者沒有;
  • 調用的區別,函數可以在 sql 語句中直接調用,而存儲過程必須單獨調用;
  • 函數一般情況下是用來計算並返回一個計算結果,而存儲過程一般是用來完成特定的數據操作(比如修改、插入數據庫表或執行某些 DDL 語句等等)

21. 程序包

a. 簡介

  • 包(Package)是一組相關過程、函數、變量、常量、類型和遊標等 PL/SQL 程序設計元素的組合。包具有面向對象設計的特點,是對這些 PL/SQL 程序設計元素的封裝。包包括兩部分:定義一個包(包頭)、實現一個包(包體);只有當定義包後才能實現包體.其中包體中的函數名與過程名須和包頭中定義的函數、過程一樣。
  1. 包和包體必須有相同的名字;
  2. 包的開始沒有 begin 語句,與存儲過程和函數不同;
  3. 在包頭部分定義函數和過程的名稱和參數,具體實現在包體中定義;
  4. 在包體內聲明常量、變量、類型定義、異常、及遊標時不使用 declare;
  5. 包體內的過程和函數的定義不要 create or replace 語句;
  6. 包定義和包體兩者分離。

b. 創建包

i. 定義包(包頭)

CREATE [OR REPLACE] PACKAGE <包名> AS|IS 
	--公共類型和對象聲明
	--子程序說明
END;

【注意】包的定義中不需要 begin 關鍵字;函數和過程的定義也不需要 create 
or replace。另外,包中定義的變量、常量在包體中可直接使用。

【示例語法】
create or replace package <Package_name> as
	
	-- 定義自定義類型
	type <TypeName> is <Datatype>;
	
	-- 公共常量定義
	<ConstantName> constant <Datatype> := <Value>;--聲明常量
	
	-- 公共變量定義
	<VariableName> <Datatype>; --數據類型
	
	-- 公共函數或存儲過程定義
	function <FunctionName>(<Parameter> <Datatype>) return <Datatype>; --函數
	procedure <ProcedurenName>(<Parameter> <Datatype>); --存儲過程 

end [Package_name];

ii. 實現包(包體)

CREATE [OR REPLACE] PACKAGE BODY <包名> AS 
	--公共類型和對象聲明
 	 [BEGIN]
	-初始化語句
END;

【示例語法】
create or replace package body <Package_name> as

	-- 私有自定義類型(包內可用)
	type <TypeName> is <Datatype>;
	
	-- 私有常量(包內可用)
	<ConstantName> constant <Datatype> := <Value>
	
	-- 私有變量(包內可用)
	<VariableName> <Datatype>;
	
	-- 函數或存儲過程的實現
	function <FunctionName>(<Parameter> <Datatype>) return <Datatype> as --函數實現
	<LocalVariable> <Datatype>;

	begin
		<Statement>;
		return(<Result>);
	end;
	procedure <ProcedureName>(<Parameter> <Datatype>) as --存儲過程實<LocalVariable> <Datatype>;
	begin
		<Statement>;
	end;
	begin
	--初始化包體
		<Statement>;
	end [Package_name];

iii. 應用

/*
創建一個包含有變量、存儲過程和函數的包;其中
存儲過程可根據員工號查詢並輸出員工的姓名和工資
函數中利用定義的變量,然後則根據員工號查詢出該員工獎金並返回
*/
--定義包
create or replace package pack_1 as

--定義存儲過程
procedure pro_1(p_empno emp.empno%type);

--定義函數
function fun_1(p_empno emp.empno%type) return number;
end;

-- 【切記】需要先定義並編譯(執行)包,之後再實現並編譯(執行)包體
--實現包(包體)
create or replace package body pack_1 as

--定義包變量
p_comm number(7,2);
procedure pro_1(p_empno emp.empno%type) as
	p_ename emp.ename%type;
	p_sal emp.sal%type;
	begin
		select ename,sal into p_ename,p_sal from emp where empno=p_empno;
		dbms_output.put_line('員工號爲:'||p_empno||',對應的員工姓名爲:'|| p_ename||',工資爲:'||p_sal);
	end;
	function fun_1(p_empno emp.empno%type) return number as f_comm emp.comm%type;
	begin
		select nvl(comm,0) into p_comm from emp where empno=p_empno;
		return p_comm;
	end;
end;

--打開輸出
set serveroutput on;

begin
	--調用包中的存儲過程
	pack_1.pro_1(7499);
	--調用包中的函數
	dbms_output.put_line('7499對應的獎金爲:'||pack_1.fun_1(7499));
end;

iv. 程序中調用包

  1. 創建包
/*
利用包編寫一個帶有遊標類型輸出參數的存儲過程
根據部門編號查詢該部門下的員工
*/
create or replace package pack_2 as
	--自定義一個遊標類型
	type empCursor is ref cursor;
	procedure pro_1(p_deptno in emp.deptno%type, p_empCursor out empCursor);
end;

create or replace package body pack_2 as
	procedure pro_1(p_deptno in emp.deptno%type, p_empCursor out
empCursor) as
	begin
		open p_empCursor for select * from emp where deptno=p_deptno;
	end;
end; 
  1. 程序中調用上述包
package cn.regino;

import Java.sql.CallableStatement;
import Java.sql.Connection;
import Java.sql.DriverManager;
import Java.sql.ResultSet;
import Java.sql.SQLException;
import Oracle.jdbc.OracleCallableStatement;
import Oracle.jdbc.OracleTypes;

public class TestPackage {
	public static void main(String[] args) {
		Connection conn = null;
		CallableStatement call = null;
		try {
			Class.forName("Oracle.jdbc.OracleDriver");
			String url = "jdbc:Oracle:thin:@localhost:1521:orcl";
			conn = DriverManager.getConnection(url, "regino", "regino");
			call = conn.prepareCall("{call 
			pack_2.pro_1(?,?)}");
			//設置輸入型參數
			call.setInt(1, 10);
			//註冊輸出型參數(類型爲遊標)
			call.registerOutParameter(2, OracleTypes.CURSOR);
			call.execute();
			ResultSet rs = ((OracleCallableStatement)call).getCursor(2);
			if(rs != null){
				while(rs.next()){
					System.out.println("員工號爲:" + rs.getInt(1) + ",姓名爲:" + rs.getString(2));
				}
				rs.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(call != null){
					call.close();
				}
				if(conn != null){
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} 
		} 
	} 
}

c. 刪除包

【語法】
DROP PACKAGE <包名>;

【示例】
drop package pack_1;

22. 觸發器

a. 語法

【語法】
	CREATE [OR REPLACE] TRIGGER <觸發器名>
	BEFORE|AFTER
	INSERT|DELETE|UPDATE [OF <列名>] ON <表名>
	[FOR EACH ROW]
	<pl/sql>

【說明】
	關鍵字"BEFORE"在操作完成前觸發;"AFTER"則是在操作完成後觸發;
	關鍵字"FOR EACH ROW"指定觸發器每行觸發一次,若不指定則爲表級觸發器.
	關鍵字"OF <列名>" 不寫表示對整個表的所有列.
	pl/sql 塊中不能使用 commit;

【特殊變量】
	:new --爲一個引用最新的行值;
	:old --爲一個引用以前的行值; 
這些變量只有在使用了關鍵字 "FOR EACH ROW"時才存在.update 語句兩個
都有,insert 只有:new ,delect 只有:old;

b. 行級觸發器

  • 【示例 1】漲工資
/*
觸發器使用:給員工漲工資(漲後工資應該大於漲前)後,在後臺輸出更新
前和更新後的工資
*/
create or replace trigger tri_emp_upd_sal
	after
	update of sal on emp
	for each row
begin
	if :old.sal < :new.sal then
		dbms_output.put_line('更新前工資爲:'||:old.sal||',更新後工資爲:'||:new.sal);
	else
		raise_application_error(-20002,'工資不能越漲越低!');
	end if;
end; 
 
-- 更新工資值,並觸發行級觸發器
update emp set sal = 8888 where empno = 1002; 
  • 【示例 2】觸發器+序列實現主鍵自增長
/*
觸發器使用:給emp表的empno添加觸發器,在插入記錄時自動填入值
*/
-- 1、創建序列
create sequence seq_emp_empno;
-- 2、創建觸發器
create or replace trigger tri_emp_ins_empno
	before
	insert on emp
	for each row
begin
	-- 給將要插入表的記錄:new 中的empno設置sequence中的值
	select seq_emp_empno.nextval into :new.empno from dual;
end;
 
-- 新增員工數據,測試觸發器+序列的組合使用
insert into emp(ename,sal) values('regino002',2000);
commit;

c. 表級觸發器

/*
觸發器使用:刪除表的同時備份表數據到另一張備份表
*/
-- 1、從emp表結果中創建一張表並複製數據
create table emp2 as select * from emp;

-- 2、創建備份表emp_bak
create table emp_bak as select * from emp2 where 1=2;

-- 3、創建表觸發器,當對錶操作時觸發
create or replace trigger tri_emp2_del
	before
	delete on emp2
begin
	-- 將emp2表中的數據備份到emp_bak
	insert into emp_bak select * from emp2;
end; 

-- 4、測試刪除emp2表的數據
delete from emp2;
select * from emp2;
select * from emp_bak;

d. 開啓禁用觸發器

【禁用某個觸發器】
ALTER TRIGGER <觸發器名> DISABLE
【示例】
alter trigger tri_emp_upd_sal disable;
update emp set sal = 8888 where empno = 1002;


【重新啓用觸發器】
ALTER TRIGGER <觸發器名> ENABLE
【示例】
alter trigger tri_emp_upd_sal enable;
update emp set sal = 8888 where empno = 1002;


【禁用表的所有觸發器】
ALTER TABLE <表名> DISABLE ALL TRIGGERS;
【示例】
alter table emp disable all triggers;


【啓用表的所有觸發器】
ALTER TABLE <表名> ENABLE ALL TRIGGERS;
【示例】
alter table emp enable all triggers;


【刪除觸發器】
DROP TRIGGER <觸發器名>;
【示例】
drop trigger tri_emp_upd_sal;

23. 數據字典

  • 數據字典對用戶來說是一組只讀的表;數據字典內容包括,數據庫中所有模式對象的信息。Oracle 數據字典中,對象名稱多數以"USER.",“ALL.”,“DBA.”。前綴"USER."視圖中記錄通常記錄執行查詢的帳戶所擁有的對象的信息,"ALL."視圖中記錄包括"USER"記錄和授權至 PUBLIC 或用戶的對象的信息,"DBA."視圖包含所有數據庫對象,而不管其所有者。
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

24. 角色

  • Oracle 提供了三種標準的角色(role):CONNECT、RESOURCE 和 DBA。
    1. CONNECT Role(連接角色)
      臨時用戶,特別是那些不需要建表的用戶,通常只賦予他們 CONNECT role。CONNECT 是使用 Oracle 的簡單權限,這種權限只有在對其他用戶的表有訪問權時,包括 select、insert、update 和 delete 等,纔會變得有意義。
    2. RESOURCE Role(資源角色)
      更可靠和正式的數據庫用戶可以授予 RESOURCE role。RESOURCE 提供給用戶另外的權限以創建他們自己的表、序列、過程、觸發器、索引和簇。
    3. DBA Role(數據庫管理員角色)
      DBA role 擁有所有的系統權限–包括無限制的空間限額和給其他用戶授予
      各種權限的能力。
  • 除此以上角色外;還可以自行創建角色。用戶創建的 role 可以由表或系統權限或兩者的組合構成。爲了創建 role,用戶必須具有 CREATE ROLE 系統權限。

a. 創建角色

  • 創建角色後,可以對角色授予權限;授權的語法和前面授權給用戶的語法相同。
【語法】
CREATE ROLE <role_name>;

【示例】
-- system 用戶登錄,授予regino 創建角色的權限
grant create role to regino;

-- 創建角色
create role role_regino;

-- 授予emp的select 操作權限給role_regino角色
grant select on emp to role_regino;

-- 給scott用戶授予role_regino的角色
grant role_regino to scott;

b. 刪除角色

【語法】
DROP ROLE <role_name>;

【示例】
drop role role_regino;

25. 閃回

a. 閃回簡介

  • 在 Oracle 的操作工程中,會不可避免地出現操作失誤或者用戶失誤,例如不小心刪除了一個表等,這些失誤和錯誤可能會造成重要數據的丟失,最終導致 Oracle 數據庫停止。
  • 在傳統操作上,當發生數據丟失、數據錯誤問題時,解決的主要辦法是數據的導入導出、備份恢復技術,這些方法都需要在發生錯誤前,有一個正確的備份才能進行恢復。爲了減少這方面的損失,Oracle 提供了閃回技術。有了閃回技術,就可以實現數據的快速恢復,而且不需要數據備份
  • 閃回特點
    • 傳統的恢復技術緩慢:它是整個數據庫或者一個文件恢復,不只恢復損壞的數據在數據庫日誌中每個修改都必須被檢查;
    • 閃回速度快:通過行和事務把改變編入索引,僅僅改變了的數據會被恢復;
    • 閃回命令容易,沒有複雜步驟。

b. 閃回類型

  • 主要有三種閃回:閃回表(flashback table)、閃回刪除(flashback drop)、閃回數據庫(flashback database);一般情況下對數據庫的閃回需要配置閃回數據庫,然後自動產生閃回日誌;再根據閃回日誌恢復數據庫。

c. 閃回查詢

  • 根據閃回日誌可以快速查詢在某個時間點的數據。
--查看 10 秒之前的 emp 表
select * from emp as of timestamp sysdate - interval '10' second;
select * from emp as of scn timestamp_to_scn(sysdate - interval '10'
second);
【說明】
as of timestamp 是固定寫法,查詢某個時間點對應的數據
as of scn 查詢某 scn 對應的數據
sysdate – interval10second 是時間值的計算

--通過查詢某個時間的數據來更新現有數據
--將 7499 員工的姓名更新爲 5 分鐘之前的姓名
update emp e set ename = 
(select ename from emp 
as of timestamp systimestamp - interval '5' minute where
empno=e.empno) 
where empno=7499;

d. 閃回表

  • 閃回表(flashback table)實際上是將表中的數據快速恢復到過去的一個焦點或者系統改變號 SCN 上;對進行表閃回的表必須 row movement 爲 enable。
    • SCN: System Change Number.
    • 實現表的閃回,需要使用到與撤銷表空間相關的 undo 信息,通過 show parameter undo 命令可以瞭解這些信息。
    • conn sys/orcl as sysdba
    • show parameters undo; // undo 表空間
    • alter system set undo_retention=1200 scope=both;
  • undo_retention:數據保留時間長度(默認是 900 秒)
  • scope 參數的值:
  • momory-當前 session 中有效
  • spfile: 修改配置文件,但當前會話中無效
  • both:當前會話有效,同時修改配置文件
  • undo 表空間:保存了所有的操作記錄(2G 的空間)因爲有了該表空間纔可以進行閃回
【語法】
flashback table [schema.]table_name[,...n] to {[scn] | [timestamp] [[enable | disable] triggers]};

【說明】
scn:表示通過系統改變號進行閃回;scn 系統改變號一般和系統時間相對應;查看當前系統時間和所對應系統 scn:
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),
timestamp_to_scn(sysdate) from dual;

timestamp:表示通過時間戳的形式來進行閃回;
enable|disable triggers:表示觸發器恢復之後的狀態,默認爲 disable。
 
rowid 這個僞列是 Oracle 默認提供給每一個表的,主要用於記錄每一行數據存儲的磁盤物理地址。當刪除一行記錄後,後面的記錄依次跟進上來,當需要恢復某一箇中間的行時,就需要行具備行移動功能(alter table <表名> 
enable row movement;)

【示例】
-- 授權用戶閃回表的權限
grant flashback any table to regino;

-- 查看當前時間點或scn號
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),
timestamp_to_scn(sysdate) from dual;

-- 刪除數據
delete from emp where empno = 7449;
commit;

--允許行移動
alter table emp enable row movement;

-- 方式一;使用時間點閃回表
flashback table emp to timestamp to_timestamp('時間格式字符串','yyyy-mm-dd HH24:mi:ss');

-- 方式二;使用SCN閃回表
flashback table emp to scn SCN號;

e. 閃回刪除

  • 閃回刪除(flashback drop)。當整個表被刪除並在回收站查詢到的話;可以對錶進行閃回。show recyclebin:可以顯示當前用戶 recyclebin 中的表。系統參數 recyclebin 控制表刪除後是否到回收站,show parameter recyclebin 可以查看該參數的狀態。對於系統參數的修改有兩種,全局的修改和會話的修改:
    (1)alter system set param_name=param_value;
    (2)alter session set param_name=param_value;
  • show recyclebin; --查看回收站
  • purge recyclebin; --清空回收站
【語法】
flashback table table_name to before drop [rename to new_name]; 

【說明】
rename to new_name:如果在刪除原表之後又重新創建了一個一樣名稱的表,那麼恢復回收站的表時可以對錶名進行重命名

【示例】
-- 刪除表
drop table emp;

-- 恢復表
flashback table emp to before drop;

26. 數據備份與恢復

a. 數據備份

--全表備份
exp regino/regino@orcl file=d:\database\Oracle_data\regino.dmp 
full=y

--指定表備份
exp regino/regino@orcl 
file=d:\database\Oracle_data\regino_emp_dept.dmp tables=(emp,dept)

【說明】full:完整導出數據庫非空數據表,一般使用 system 具有管理員權限的用戶在命令行下進行操作。

b. 數據恢復

--全表恢復
imp regino/regino@orcl ignore=y 
file=d:\database\Oracle_data\regino.dmp full=y

--指定表恢復
imp regino/regino@orcl ignore=y 
file=d:\database\Oracle_data\regino_emp_dept.dmp tables=(emp,dept)

【說明】ignore:忽略創建錯誤

27. 性能優化

  1. 查兩張以上表時,把記錄少的放在右邊
  2. WHERE 子句中的連接順序
    • Oracle 採用自上而下的順序解析 WHERE 子句,根據這個原則,那些可以過濾掉最大數量記錄的條件應寫在 WHERE 子句最後。
    • 例如:查詢員工的編號,姓名,工資,部門名
      如果 emp.sal>1500 能過濾掉半數記錄的話,
      select emp.empno,emp.ename,emp.sal,dept.dname
      from emp,dept
      where (emp.deptno = dept.deptno) and (emp.sal > 1500)
  3. SELECT 子句中避免使用*號
    • Oracle 在解析的過程中,會將*依次轉換成所有的列名,這個工作是通過查詢數據字典完成的,這意味着將耗費更多的時間
  4. 避免對大表進行無條件或無索引的的掃描
  5. 清空表時用 TRUNCATE 替代 DELETE
  6. 儘量多使用 COMMIT;因爲 COMMIT 會釋放回滾點
  7. 用索引提高查詢效率,善用索引
    • 避免在索引列上使用 NOT;因爲 Oracle 服務器遇到 NOT 後,他就會停止目前的工作,轉而執行全表掃描。
    • 避免在索引列上使用計算;WHERE 子句中,如果索引列是函數的一部分,優化器將不使用索引而使用全表掃描,這樣會變得慢
      • 例如,SAL 列上有索引,
        • 低效:
          SELECT EMPNO,ENAME
          FROM EMP
          WHERE SAL*12 > 24000;
        • 高效:
          SELECT EMPNO,ENAME
          FROM EMP
          WHERE SAL > 24000/12;
  8. 字符串型,能用=號,不用 like
    • =號表示精確比較,like 表示模糊比較
  9. 用 >= 替代 >
    • 低效:
      SELECT * FROM EMP WHERE DEPTNO > 3
      首先定位到 DEPTNO=3 的記錄並且掃描到第一個 DEPT 大於 3 的記錄
    • 高效:
      SELECT * FROM EMP WHERE DEPTNO >= 4
      直接跳到第一個 DEPT 等於 4 的記錄
  10. 用 IN 替代 OR
    • select * from emp where sal = 1500 or sal = 3000 or sal = 800;
    • select * from emp where sal in (1500,3000,800);
  11. 用 exists 代替 in;not exists 代替 not in
    • not in 字句將執行一個內部的排序和合並,任何情況下,not in 是最低效的,子查詢中全表掃描;表連接比 exists 更高效
  12. 用 UNION-ALL 替換 UNION
    • 當 SQL 語句需要 UNION 兩個查詢結果集合時,這兩個結果集合會以 UNION-ALL 的方式被合併, 然後在輸出最終結果前進行排序. 如果用 UNION ALL 替代 UNION, 這樣排序就不是必要了. 效率會因此得到提高。
  13. 避免使用耗費資源的操作
    • 帶有 DISTINCT,UNION,MINUS,INTERSECT 的 SQL 語句會啓動 SQL 引擎 執行耗費資源的排序(SORT)功能. DISTINCT 需要一次排序操作, 而其他的至少需要執行兩次排序. 通常, 帶有 UNION, MINUS , INTERSECT 的 SQL 語句都可以用其他方式重寫。
  • 最後;同樣的操作有些時候可以在程序上處理的就程序上處理,畢竟在內存中的執行速度比在硬盤上執行要高非常多。

28. 項目切換到 Oracle 數據庫

  1. 修改 applicationContext.xml 中的數據庫方言
  2. 修改 db.properties 中的數據庫驅動類和用戶名密碼
  3. 其它修改(如創建 user 表時將表名修改爲 t_user,user 是 Oracle 數據庫的關鍵字)

原文鏈接:https://qwert.blog.csdn.net/article/details/105830866

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章