Oracle數據庫--從入門到裝逼

目錄

1 Oracle基礎

1.1 Oracle簡介

1.1.1 什麼是Oracle

  Oracle數據庫系統是美國Oracle公司(甲骨文)提供的以分佈式數據庫爲核心的一組軟件產品,是目前最流行的客戶服務器(CLIENT/或B/S體系結構的數據庫之一。
  Oracle通常應用於大型系統的數據庫產品。
  Oracle數據庫是目前世界上使用最爲廣泛的數據庫管理系統,作爲一個通用的數據庫系統,它具有完整的數據管理功能;作爲一個關係數據庫,它是一個完備關係的產品;作爲分佈式數據庫它實現了分佈式處理功能
Oracle數據庫具有以下特點:

  1. 支持多用戶、大事務量的事務處理,處理併發能力狠強
  2. 數據安全性和完整性控制,在安全設計方面沒有任何數據庫可與之相比
  3. 支持分佈式數據處理,一個實例部署到多臺機器時由Oracle自身實現分佈式
  4. 可移植性,在不同系統中的數據文件類型是一致的,可在不同版本系統做數據移植

1.1.2 Oracle體系結構

1.數據庫
  Oracle數據庫是數據的物理存儲。這就包括(數據文件ora或者dbf、控制文件、聯機日誌、參數文件)。其實Oracle數據庫的概念和其它數據庫不一樣,這裏的數據庫是一個操作系統只有一個庫(而MySQL是在一個實例下可創建多個庫)。可以看作是Oracle就只有一個大數據庫。
2.實例
  一個Oracle實例(OracleInstance)由一系列的後臺進程(BackguoundProcesses)和內存結構(MemoryStructures)組成。一個數據庫可以有n個實例(一般使用時通常只建一個實例)。
3.數據文件(dbf)
  數據文件是數據庫的物理存儲單位。數據庫的數據是存儲在表空間中的,數據真正是在某一個或者多個數據文件(dbf)中。而一個表空間可以由一個或多個數據文件組成,一個數據文件只能屬於一個表空間。一旦數據文件被加入到某個表空間後,就不能刪除這個文件,如果要刪除某個數據文件,只能刪除其所屬於的表空間纔行。
4.表空間
  表空間是Oracle對物理數據庫上相關數據文件(ora或者dbf文件)的邏輯映射。一個數據庫在邏輯上被劃分成一到若干個表空間,每個表空間包含了在邏輯上相關聯的一組結構。每個數據庫至少有一個表空間(稱之爲system表空間)。
  每個表空間由同一磁盤上的一個或多個文件組成,這些文件叫數據文件(datafile)。一個數據文件只能屬於一個表空間。
在這裏插入圖片描述
注:表的數據,是由用戶放入某一個表空間的,而這個表空間會隨機把這些表數據放到一個或者多個數據文件(dbf)中。Oracle是由用戶和表空間對數據進行管理和存放的。但是表不是由表空間去查詢,而是由用戶去查。因爲不同用戶可以在同一個表空間建立同一個名字的表,這裏就是以用戶作區分了。
在這裏插入圖片描述
5.用戶
  用戶是在表空間下建立的。用戶登陸後只能看到和操作自己的表, Oracle的用戶與MySQL的數據庫類似,每建立一個應用需要創建一個用戶。
在這裏插入圖片描述

1.2 Oracle安裝與配置

本次演示安裝Oracle 11g,安裝環境爲Windows10 64位專業版

1.2.1 安裝前準備

下載地址:https://www.oracle.com/database/technologies/oracle-database-software-downloads.html
在這裏插入圖片描述
下載的這兩個文件,將兩個壓縮包解壓到同一路徑下,使解壓後的文件合併,解壓後steup.exe安裝
在這裏插入圖片描述
安裝前務必要關閉各種殺毒軟件,安全衛士

1.2.2 安裝步驟

安裝步驟可參考https://www.cnblogs.com/gengshao/p/10764248.html

1.2.3 可能出現的問題及處理

1.2.3.1 Environment variable:“PATH”

Environment variable: “PATH” - This test checks whether the length of
the environment variable “PATH” does not exceed the recommended
length. 預期值 : 1023 實際值 : 1184 錯誤列表: PRVF-3929 : Adding the Oracle
binary location to the PATH environment variable will exceed the OS
length limit of [ “1023” ] for the variable on the node “ABC” - Cause:
The installer needs to update the PATH environment variable to include
the value “%ORACLE_HOME%/bin;”. However, doing so will cause PATH to
exceed the maximum allowable length that this operating system allows.

  • Action: Ensure that the sum of the lengths of your current PATH environment variable and that of “%ORACLE_HOME%/bin;” does not exceed
    the operating system limit. Restart the installer after correcting the
    setting for environment variable

改 cvu_prereq.xml 裏面 的配置,cvu_prereq.xml 文件在oracle安裝解壓包database\stage\cvu目錄中找到自己對於的電腦系統,我的是win10的,沒有的可以填加下面的內容(在CERTIFIED_SYSTEMS標籤下)

<OPERATING_SYSTEM RELEASE="6.2">
	<VERSION VALUE="3"/>
	<ARCHITECTURE VALUE="64-bit"/>
	<NAME VALUE="Windows 10"/>
	<ENV_VAR_LIST>
		<ENV_VAR NAME="PATH" MAX_LENGTH="2023" />
	</ENV_VAR_LIST>
</OPERATING_SYSTEM>

把1023改大 比如2023 然後退出安裝程序重新安裝下就ok了

1.2.3.2 執行先決條件失敗

在這裏插入圖片描述
處理方式請參考https://www.cnblogs.com/xiaoxiaoweng/p/7277969.html

1.2.3.3 實例化EM配置文件時出錯

安裝時如果沒有關閉殺毒軟件便可能出現此問題
在這裏插入圖片描述
處理方式1:重新配置EM
配置方式可參考https://blog.csdn.net/ajphsb0849/article/details/101285448
處理方式2:卸載Oracle,重新安裝
卸載方式可參考
https://blog.csdn.net/Fisher_lht/article/details/78797141
https://blog.csdn.net/m0_38025207/article/details/80717071

1.2.4 可視化工具PL/SQL Developer

PL/SQL Developer下載、安裝、使用教程請參考
https://blog.csdn.net/sinat_34104446/article/details/80330021

1.2.5 創建/刪除表空間

create tablespace hellospace	--創建名爲hellospace的表空間
datafile 'E:\Repository\hellospace.dbf'	--表空間對應的數據文件是hellospace.dbf
size 100 m	--數據文件的大小是100兆
autoextend on	--當數據超過100兆時自動增長
next 10 m;	--數據文件自動增長每次增長10兆
drop tablespace hellospace;	--刪除表空間hellospace,不會關聯刪除數據文件

1.2.6 創建用戶

create user bigboss	--創建名爲bigboss的用戶
identified by 666666	--用戶密碼是666666
default tablespace hellospace;	--該用戶默認的表空間是hellospace,一個表空間下可創建多個用戶

1.2.7 用戶授權

grant dba to bigboss;	--給bigboss用戶添加dba權限

1.3 表的創建、修改與刪除

1.3.1 創建表

語法:

CREATE TABLE 表名稱(
    字段名 類型(長度) primary key,
    字段名 類型(長度),
    .......
);

常用數據類型說明:

  1. 字符型
    CHAR:固定長度的字符類型,最多存儲 2000 個字節,若存入的字節數不夠設定的字節長度,會以空格補齊,取出時也會帶空格
    VARCHAR2:可變長度的字符類型,最多存儲 4000 個字節,類似MySQL的varchar
    LONG : 大文本類型,最大可以存儲 2個G,類似MySQL的text類型,可存儲長篇文章
  2. 數值型
    NUMBER:數值類型
    例如:
      NUMBER(5) 最大可以存的數爲 99999
      NUMBER(5,2)最大可以存的數爲 999.99
  3. 日期型
    DATE:日期時間型,精確到秒
    TIMESTAMP:精確到秒的小數點後9位
  4. 二進制型(大數據類型)
    CLOB : 存儲字符,最大可以存4個G,當字符長度超過LONG的長度時,可使用該字段
    BLOB:存儲圖像、聲音、視頻等二進制數據,最多可以存4個G
    實例:
create table hero (
    id number primary key,
    name varchar2(30),
    price number(10,2),
    birthday date
);

1.3.2 修改表

增加字段語法:

ALTER TABLE 表名稱 ADD( 
    列名1 類型 [DEFAULT 默認值],
    列名1 類型 [DEFAULT 默認值],
    ...
);

例如:給hero表追加兩個字段

ALTER TABLE HERO ADD(
  REMARKS VARCHAR2(20),
  SEX VARCHAR2(30)
);

修改字段類型或長度語法:

ALTER TABLE 表名稱 MODIFY( 
    列名1 類型 [DEFAULT 默認值],
    列名1 類型 [DEFAULT 默認值],
    ...
);

例如:修改hero表剛添加的兩個字段的類型或長度

ALTER TABLE HERO MODIFY(
    REMARKS VARCHAR2(50),
    SEX CHAR(1)
);

修改字段名語法:

ALTER TABLE 表名稱 RENAME COLUMN 原列名 TO 新列名

例如:修改hero中的sex字段名修改爲gender

ALTER TABLE HERO RENAME COLUMN SEX TO GENDER

刪除字段名語法:

--刪除一個字段
ALTER TABLE 表名稱 DROP COLUMN 列名
--刪除多個字段
ALTER TABLE 表名稱 DROP (列名 1, 列名 2...)

1.3.3 刪除表

刪除表語法:

DROP TABLE 表名稱

1.4 數據增刪改

1.4.1 插入數據

插入數據語法:

INSERT INTO 表名 [列名1,列名2,...)] VALUES(1,2,...);
COMMIT;

例如:

INSERT INTO HERO VALUES (1,'典韋',9000.50,sysdate,'戰士','1');
COMMIT;

注:語句中的 sysdate 是系統變量用於獲取當前日期,執行INSERT後一定要再執行commit提交事務hero表插入數據

1.4.2 修改數據

修改數據語法:

UPDATE 表名 SET 列名1=1,列名2=2,...WHERE 修改條件;
COMMIT;

需求:將hero表中id爲1的英雄的birthday日期更改爲三天前的日期

update hero set birthday=birthday-3 where id=1;
commit;

1.4.3 刪除數據

刪除語法1:

DELETE FROM 表名 WHERE 刪除條件;
COMMIT;

需求:刪除hero表中id爲1的數據

delete from hero where id=1;
commit;

刪除語法2:

TRUNCATE TABLE 表名稱;

需求:刪除hero表中所有數據

delete from hero;
commit;

TRUNCATE TABLE HERO;

比較truncat與delete實現數據刪除:

  • delete刪除的數據在commit之前可以執行rollback回滾,delete操作會將被刪除的數據放入表空間的回滾段,然後若執行rollback,會將數據從回滾段恢復到表中,若執行commit,則將數據從回滾段刪除最終刪除數據。delete刪除可能產生碎片,並且不釋放空間
  • truncate是先摧毀表結構,再重構表結構,truncate操作可釋放空間,truncate刪除的執行效率更高。

1.5 Java通過JDBC操作Oracle

1.5.1 創建工程,引入驅動包

查找驅動包請參考https://www.cnblogs.com/tv151579/archive/2013/02/26/2933405.html
在eclipse中創建 java 工程 ,建立 lib 文件夾,將驅動包拷貝到此文件夾,然後 add→build path

1.5.2 編寫數據訪問類

package com.example.dao;
import java.sql.SQLException;
/** 基本數據訪問類 */
public class BaseDao {
    //加載驅動
    static {
        try { 
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException e) { 
            e.printStackTrace();
       }
    }
    /** 獲取數據庫連接 */
    public static java.sql.Connection getConnection() throws SQLException {
        return  java.sql.DriverManager.getConnection(
        "jdbc:oracle:thin:@192.168.80.10:1521:orcl", "system",  "123456");
    }
    /** 關閉資源 */
    public static void closeAll (java.sql.ResultSet rs, java.sql.Statement stmt,
                            java.sql.Connection conn){
        //關閉結果集
        if(rs!=null){
            try { 
                rs.close();
            } catch (SQLException  e)  {
                e.printStackTrace();
            }
        }
        //關閉執行對象
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //關閉執行對象
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

注:jdbc:oracle:[email protected]:1521:orcl中的thin表示瘦連接,此連接方式不需要連接方具備Oracle客戶端或相關環境
瘦連接
  大概是Java應用程序、JSP、EJB (Enterprise Java Beans,企業級Java Bean)等最常用的一種連接方式了,它爲不直接訪問Oracle庫文件而創建代碼提供了許多便利。
胖連接:
  和Oracle JDBC瘦驅動器相比,Oracle調用接口(OCI)驅動與Oracle C/C++庫耦合得更加緊密。如果要使用Oracle調用接口,我們需要保證PATH、CLASSPATH和LD_LIBRARY_PATH環境變量映射到了Oracle庫。這些庫都需要基於相同的物理平臺或通過存儲區域網絡(SAN)映射,如UNIX中的NFS

1.5.3 增刪改代碼編寫

1.創建實體類

package com.example.entity;
import java.util.Date;

/** hero實體類 */
public class Hero {
private Long id;//編號
private String name;//英雄名稱 
private Double price;//價格 
private Date birthday;//創建時間 
private String remarks;//備註信息 

public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Double getPrice() {return price;}
public void setPrice(Double price) {this.price= price;}
public Date getBirthday() {return birthday;}
public void setBirthday(Date birthday) {this.birthday= birthday;}
public String getRemarks() {return remarks;}
public void setRemarks(String remarks) {this.remarks= remarks;}
}

創建 Dao 類實現增刪改

package com.example.dao;
import java.sql.SQLException;
import com.example.entity.Hero;
/** 數據訪問類 */
public class HeroDao {
    /** 新增 */
    public static void add(Hero hero){
        java.sql.Connection conn=null;
        java.sql.PreparedStatement stmt=null;//預處理對象,防止注入攻擊
        try { 
            conn=BaseDao.getConnection();
            stmt=conn.prepareStatement("insert into hero values(?,?,?,?,?)");
            stmt.setLong(1, hero.getId());
            stmt.setString(2, hero.getName());
            stmt.setDouble(3, hero.getPrice());
            stmt.setBirthday(4, new java.sql.Date(hero.getBirthday().getTime()));
            stmt.setRemarks(5, hero.getRemarks());
            stmt.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeAll(null, stmt, conn);
        }
    }  
    /** 修改 */
    public static void update(Hero hero){  
        java.sql.Connection conn=null;
        java.sql.PreparedStatement stmt=null;
        try {
            conn=BaseDao.getConnection();
            stmt=conn.prepareStatement("update hero set name=?,price=?,birthday=?,"
                    + "remarks=? where id=?");
            stmt.setString(1, hero.getName());
            stmt.setDouble(2, hero.getPrice());
            stmt.setBirthday(3, new java.sql.Date(hero.getBirthday().getTime()));
            stmt.setRemarks(4, hero.getRemarks());
            stmt.setLong(5, hero.getId());
            stmt.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeAll(null, stmt, conn);
        }
    }
    /** 刪除 */
    public static void delete(Long id){
        java.sql.Connection conn=null;
        java.sql.PreparedStatement stmt=null;
        try {
            conn=BaseDao.getConnection();
            stmt=conn.prepareStatement("delete from hero where id=?");
            stmt.setLong(1, id); stmt.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeAll(null, stmt, conn);
        }
    } 
}

1.6 數據庫備份與恢復

1.6.1 整庫導入與導出

整庫導出命令:(直接在cmd窗口執行)

exp system/123456 full=y

注:添加參數full=y就是整庫導出
在這裏插入圖片描述
執行命令後會在當前目錄下生成一個叫EXPDAT.DMP ,此文件爲備份文件。
如果想指定備份文件的名稱,則添加file 參數即可,命令如下

exp system/123456 file=文件名 full=y

整庫導入命令

imp system/123456 full=y

此命令如果不指定file 參數,則默認用備份文件 EXPDAT.DMP 進行導入如果指定file 參數,則按照file指定的備份文件進行恢復

imp system/123456 full=y file=hero.dmp

1.6.2 按用戶導入與導出

按用戶導出

exp system/123456 owner=用戶名 file=XXX.dmp

按用戶導入

imp system/123456 file=XXX.dmp fromuser=用戶名

1.6.3 按表導入與導出

按表導出

exp 用戶名/密碼 file=XXX.dmp tables=表名1,表名2......

用tables參數指定需要導出的表,如果有多個表用逗號分割即可參數指定需要導出的表,如果有多個表用逗號分割即可按表導入

imp 用戶名/密碼 file=XXX.dmp tables=表名1,表名2......

2 Oracle查詢

這裏以項目案例《自來水公司收費系統》爲場景來講述Oracle的查詢
項目介紹與需求分析:
  某自來水公司爲更好地對自來水收費進行規範化管理,決定委託開發《自來水公司收費系統》。考慮到自來水業務數量龐大,數據併發量高,決定數據庫採用ORACLE數據庫。主要功能包括:
1.基礎信息管理:
(1)業主類型設置
(2)價格設置
(3)區域設置
(4)收費員設置
(5)地址設置
2.業主信息管理:
(1)業主信息維護
(2)業主信息查詢
3.收費管理:
(1)抄表登記
(2)收費登記
(3)收費記錄查詢
(4)欠費用戶清單
4.統計分析:
(1)收費日報單
(2)收費月報表
表結構設計:
1.業主類型表(T_OWNERTYPE)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
NAME VARCHAR2(30) 類型名稱

2.價格表(T_PRICETABLE)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
PRICE NUMBER(10,2) 價格
OWNERTYPEID NUMBER 業主類型
MINNUM NUMBER(10,2) 區間數開始值
MAXNUM NUMBER(10,2) 區間數截止值

3.區域表(T_AREA)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
NAME VARCHAR2(30) 區域名稱

4.收費員表(T_OPERATOR)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
NAME VARCHAR2(30) 操作員名稱

5.地址表(T_ADDRESS)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
NAME VARCHAR2(30) 地址名稱
AREAID NUMBER 區域
OPERATORID NUMBER 操作員

6.業主表(T_OWNERS)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
NAME VARCHAR2(30) 業主名稱
ADDRESSID NUMBER 地址ID
HOUSENUMBER NUMBER 門牌號
WATERMETER VARCHAR2(30) 水錶編號
ADDDATE DATE 登記日期
OWNERTYPEID NUMBER 業主類型ID

7.收費臺賬(T_ACCOUNT)

字段名 類型(位數) 是否必填 說明
ID NUMBER 主鍵
OWNERID NUMBER 業主編號
OWNERTYPEID NUMBER 業主類型
AREAID NUMBER 所在區域
YEAR CHAR(4) 賬務年份
MONTH CHAR(2) 賬務月份
NUM0 NUMBER 上月累計數
NUM1 NUMBER 本月累計數
USENUM NUMBER 本月使用數
METERUSERID NUMBER 抄表員
METERDATE DATE 抄表日期
MONEY NUMBER(10,2) 應繳金額
ISFEE CHAR(1) 是否繳費
FEEDATE DATE 繳費日期
FEEUSERID NUMBER 收費員

上述 7 張表的物理模型如下:
在這裏插入圖片描述
建表sql及測試數據:

--價格區間表
create  table t_pricetable
(
id number primary key,
price number(10,2),
ownertypeid number,
minnum number,
maxnum number
);
--業主類型
create table t_ownertype
(
id number primary key,
name varchar2(30)
);
--業主表
create table t_owners
(
id number primary key,
name varchar2(30),
addressid number,
housenumber varchar2(30),
watermeter varchar2(30),
adddate date,
ownertypeid number
);
--區域表
create table t_area
(
id number,
name varchar2(30)
);
--收費員表
create table t_operator
(
id number,
name varchar2(30)
);
--地址表
create table t_address
(
id number primary key,
name varchar2(100),
areaid number,
operatorid number
);
--賬務表--
create table t_account 
(
id number primary key,
owneruuid number,
ownertype number,
areaid number,
year char(4),
month char(2),
num0 number,
num1 number,
usenum number,
meteruser number,
meterdate date,
money number(10,2),
isfee char(1),
feedate date,
feeuser number
);
create sequence seq_account;
--業主類型
insert into t_ownertype values(1,'居民');
insert into t_ownertype values(2,'行政事業單位');
insert into t_ownertype values(3,'商業');
--地址信息
insert into t_address values( 1,'明興花園',1,1);
insert into t_address values( 2,'鑫源秋墅',1,1);
insert into t_address values( 3,'華龍苑南里小區',2,2);
insert into t_address values( 4,'河畔花園',2,2);
insert into t_address values( 5,'霍營',2,2);
insert into t_address values( 6,'回龍觀東大街',3,2);
insert into t_address values( 7,'西二旗',3,2);
--業主信息
insert into t_owners values(1,'範冰',1,'1-1','30406',to_date('2015-04-12','yyyy-MM-dd'),1 );
insert into t_owners values(2,'王強',1,'1-2','30407',to_date('2015-02-14','yyyy-MM-dd'),1 );
insert into t_owners values(3,'馬騰',1,'1-3','30408',to_date('2015-03-18','yyyy-MM-dd'),1 );
insert into t_owners values(4,'林小玲',2,'2-4','30409',to_date('2015-06-15','yyyy-MM-dd'),1 );
insert into t_owners values(5,'劉華',2,'2-5','30410',to_date('2013-09-11','yyyy-MM-dd'),1 );
insert into t_owners values(6,'劉東',2,'2-2','30411',to_date('2014-09-11','yyyy-MM-dd'),1 );
insert into t_owners values(7,'周健',3,'2-5','30433',to_date('2016-09-11','yyyy-MM-dd'),1 );
insert into t_owners values(8,'張哲',4,'2-2','30455',to_date('2016-09-11','yyyy-MM-dd'),1 );
insert into t_owners values(9,'昌平區中西醫結合醫院',5,'2-2','30422',to_date('2016-10-11','yyyy-MM-dd'),2 );
insert into t_owners values(10,'美廉美超市',5,'4-2','30423',to_date('2016-10-12','yyyy-MM-dd'),3 );
--操作員
insert into t_operator values(1,'馬小云');
insert into t_operator values(2,'李翠花');
--地區
insert into t_area values(1,'海淀');
insert into t_area values(2,'昌平');
insert into t_area values(3,'西城');
insert into t_area values(4,'東城');
insert into t_area values(5,'朝陽');
insert into t_area values(6,'玄武');
--價格表
insert into t_pricetable values(1,2.45,1,0,5);
insert into t_pricetable values(2,3.45,1,5,10);
insert into t_pricetable values(3,4.45,1,10,null);
insert into t_pricetable values(4,3.87,2,0,5);
insert into t_pricetable values(5,4.87,2,5,10);
insert into t_pricetable values(6,5.87,2,10,null);
insert into t_pricetable values(7,4.36,3,0,5);
insert into t_pricetable values(8,5.36,3,5,10);
insert into t_pricetable values(9,6.36,3,10,null);
--賬務表--
insert into t_account values( seq_account.nextval,1,1,1,'2012','01',30203,50123,0,1,sysdate,34.51,'1',to_date('2012-02-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','02',50123,60303,0,1,sysdate,23.43,'1',to_date('2012-03-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','03',60303,74111,0,1,sysdate,45.34,'1',to_date('2012-04-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','04',74111,77012,0,1,sysdate,52.54,'1',to_date('2012-05-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','05',77012,79031,0,1,sysdate,54.66,'1',to_date('2012-06-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','06',79031,80201,0,1,sysdate,76.45,'1',to_date('2012-07-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','07',80201,88331,0,1,sysdate,65.65,'1',to_date('2012-08-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','08',88331,89123,0,1,sysdate,55.67,'1',to_date('2012-09-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','09',89123,90122,0,1,sysdate,112.54,'1',to_date('2012-10-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','10',90122,93911,0,1,sysdate,76.21,'1',to_date('2012-11-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','11',93911,95012,0,1,sysdate,76.25,'1',to_date('2012-12-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,1,1,1,'2012','12',95012,99081,0,1,sysdate,44.51,'1',to_date('2013-01-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','01',30334,50433,0,1,sysdate,34.51,'1',to_date('2013-02-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','02',50433,60765,0,1,sysdate,23.43,'1',to_date('2013-03-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','03',60765,74155,0,1,sysdate,45.34,'1',to_date('2013-04-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','04',74155,77099,0,1,sysdate,52.54,'1',to_date('2013-05-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','05',77099,79076,0,1,sysdate,54.66,'1',to_date('2013-06-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','06',79076,80287,0,1,sysdate,76.45,'1',to_date('2013-07-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','07',80287,88432,0,1,sysdate,65.65,'1',to_date('2013-08-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','08',88432,89765,0,1,sysdate,55.67,'1',to_date('2013-09-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','09',89765,90567,0,1,sysdate,112.54,'1',to_date('2013-10-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','10',90567,93932,0,1,sysdate,76.21,'1',to_date('2013-11-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','11',93932,95076,0,1,sysdate,76.25,'1',to_date('2013-12-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,2,1,3,'2012','12',95076,99324,0,1,sysdate,44.51,'1',to_date('2014-01-14','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,100,1,3,'2012','12',95076,99324,0,1,sysdate,44.51,'1',to_date('2014-01-01','yyyy-MM-dd'),2 );
insert into t_account values( seq_account.nextval,101,1,3,'2012','12',95076,99324,0,1,sysdate,44.51,'1',to_date('2015-01-01','yyyy-MM-dd'),2 );

update t_account set usenum=num1-num0;
update t_account set money=usenum*2.45;
commit;

2.1 單表查詢

2.1.1 簡單條件查詢

2.1.1.1 精確查詢

需求:查詢水錶編號爲30408的業主記錄

select * from T_OWNERS where watermeter='30408';

查詢結果:
在這裏插入圖片描述

2.1.1.2 模糊查詢

需求:查詢業主名稱包含“劉”的業主記錄

select * from t_owners where name like '%劉%';

查詢結果:
在這裏插入圖片描述

2.1.1.3 and運算符

需求:查詢業主名稱包含“劉”的並且門牌號包含5的業主記錄

select * from t_owners where name like '%劉%' and housenumber like '%5%';

查詢結果:
在這裏插入圖片描述

2.1.1.4 or運算符

需求:查詢業主名稱包含“劉”的或者門牌號包含5的業主記錄

select * from t_owners where name like '%劉%' or housenumber like '%5%';

查詢結果:
在這裏插入圖片描述

2.1.1.5 and與or運算符混合使用

需求:查詢業主名稱包含“劉”的或者門牌號包含5的業主記錄,並且地址編號爲3的記錄。

select * from t_owners where (name like '%劉%' or housenumber like '%5%') and addressid=3

查詢結果:
在這裏插入圖片描述
注:因爲and的優先級比or大,所以需要用( )來改變優先級

2.1.1.6 範圍查詢

需求:查詢臺賬記錄中用水字數大於等於10000,並且小於等於20000的記錄
可以用>= 和<=來實現,語句:

select * from T_ACCOUNT where usenum>=10000 and usenum<=20000;

也可以用between … and …來實現:

select * from T_ACCOUNT where usenum between 10000 and 20000;

2.1.1.7 空值查詢

需求:查詢T_PRICETABLE表中MAXNUM爲空的記錄

select * from T_PRICETABLE t where maxnum is null;

查詢結果:
在這裏插入圖片描述
需求:查詢T_PRICETABLE 表中MAXNUM不爲空的記錄

select * from T_PRICETABLE t where maxnum is not null;

查詢結果:
在這裏插入圖片描述

2.1.2 去掉重複記錄

需求:查詢業主表中的地址ID,不重複顯示
語句:

select distinct addressid from T_OWNERS;

2.1.3 排序查詢

2.1.3.1 升序排序

需求:對T_ACCOUNT 表按使用量進行升序排序

select * from T_ACCOUNT order by usenum;

查詢結果:
在這裏插入圖片描述

2.1.3.2 降序排序

需求:對T_ACCOUNT表按使用量進行降序排序

select * from T_ACCOUNT order by usenum desc;

查詢結果:
在這裏插入圖片描述

2.1.4 基於僞列的查詢

在Oracle的表的使用過程中,實際表中還有一些附加的列,稱爲僞列。僞列就像表中的列一樣,但是在表中並不存儲。僞列只能查詢,不能進行增刪改操作。
僞列ROWID和ROWNUM:
ROWID
表中的每一行在數據文件中都有一個物理地址,ROWID僞列返回的就是該行的物理地址。使用ROWID可以快速的定位表中的某一行。ROWID值可以唯一的標識表中的一行。由於ROWID返回的是該行的物理地址,因此使用ROWID可以顯示行是如何存儲的。

select rowID,t.* from T_AREA t

查詢結果如下:
在這裏插入圖片描述
可以通過指定ROWID 來查詢記錄

select rowID,t.* from T_AREA t where ROWID='AAASvxAAGAAAADPAAA';

查詢結果如下:
在這裏插入圖片描述
ROWNUM
在查詢的結果集中,ROWNUM爲結果集中每一行標識一個行號,第一行返回1,第二行返回2,以此類推。通過ROWNUM 僞列可以限制查詢結果集中返回的行數。

select rownum,t.* from T_OWNERTYPE t;

查詢結果如下:
在這裏插入圖片描述
在分頁查詢需要用到此僞列,在2.4章節詳細講解

2.1.5 聚合統計

Oracle的聚合統計是通過分組函數來實現的,與MySQL一致。

2.1.5.1 聚合函數

2.1.5.1.1 求和sum

需求:統計2012年所有用戶的用水量總和。

select sum(usenum) from t_account where year='2012';
2.1.5.1.2 求平均avg

需求:統計2012年所有用水量(字數)的平均值。

select avg(usenum) from T_ACCOUNT where year='2012';
2.1.5.1.3 求最大值max

需求:統計2012 年最高用水量(字數)

select max(usenum) from T_ACCOUNT where year='2012';
2.1.5.1.4 求最小值min

需求:統計2012 年最低用水量(字數)

select min(usenum) from T_ACCOUNT where year='2012';
2.1.5.1.5 統計記錄個數count

需求:統計業主類型ID 爲1 的業主數量

select count(*) from T_OWNERS t where ownertypeid=1;

2.1.5.2 分組聚合group by

注:分組聚合group by查詢的列名必須是group by後面的列,或者聚合函數
需求:按區域分組統計水費合計數

select areaid,sum(money) from t_account group by areaid;

查詢結果:
在這裏插入圖片描述
需求:按區域,年份分組統計水費合計數

select year,areaid,sum(money) from t_account group by areaid,year;

2.1.5.3 分組後條件查詢having

需求:查詢水費合計大於16900的區域及水費合計

select areaid,sum(money) from t_account group by areaid having sum(money)>169000;

查詢結果:
在這裏插入圖片描述

2.2 連接查詢

2.2.1 多表內連接查詢

需求1:查詢顯示業主編號,業主名稱,業主類型名稱,如下圖:
在這裏插入圖片描述

select o.id 業主編號,o.name 業主名稱,ot.name 業主類型 from T_OWNERS o,T_OWNERTYPE ot 
where o.ownertypeid=ot.id;

需求2:查詢顯示業主編號,業主名稱、地址和業主類型:
在這裏插入圖片描述
分析:此查詢需要三表關聯查詢。分別是業主表,業主分類表和地址表

select o.id 業主編號,o.name 業主名稱,ad.name 地址,ot.name 業主類型
from T_OWNERS o,T_OWNERTYPE ot,T_ADDRESS ad
where o.ownertypeid=ot.id and o.addressid=ad.id

需求3:查詢顯示業主編號、業主名稱、地址、所屬區域、業主分類
在這裏插入圖片描述
分析:這裏需要四個表關聯查詢,比上邊多了一個區域表T_AREA

select o.id 業主編號,o.name 業主名稱,ar.name 區域, ad.name 地址, ot.name 業主類型
from T_OWNERS o ,T_OWNERTYPE ot,T_ADDRESS ad,T_AREA ar
where o.ownertypeid=ot.id and o.addressid=ad.id and ad.areaid=ar.id

需求4:查詢顯示業主編號、業主名稱、地址、所屬區域、收費員、業主分類
在這裏插入圖片描述
分析:此查詢比上邊又多了一個表T_OPERATOR

select ow.id 業主編號,ow.name 業主名稱,ad.name 地址,ar.name 所屬區域,op.name 收費員, ot.name 業主類型
from T_OWNERS ow,T_OWNERTYPE ot,T_ADDRESS ad,T_AREA ar,T_OPERATOR op
where ow.ownertypeid=ot.id and ow.addressid=ad.id and ad.areaid=ar.id and ad.operatorid=op.id

2.2.2 左外連接查詢

需求:查詢業主的賬務記錄,顯示業主編號、名稱、年、月、金額。如果此業主沒有賬務記錄也要列出姓名。
在這裏插入圖片描述
分析:要查詢這個結果,需要用到T_OWNERS(業主表),T_ACCOUNT(臺賬表)按照查詢結果,業主表爲左表、賬務表爲右表。
按照SQL1999標準的語法,查詢語句如下:

SELECT ow.id,ow.name,ac.year ,ac.month,ac.money
FROM T_OWNERS ow left join T_ACCOUNT ac
on ow.id=ac.owneruuid

按照Oracle提供的語法,就很簡單了:

SELECT ow.id,ow.name,ac.year ,ac.month,ac.money 
FROM T_OWNERS ow,T_ACCOUNT ac
WHERE ow.id=ac.owneruuid(+)

注:如果是左外連接,就在右表所在的條件一端填上(+)

2.2.3 右外連接查詢

需求:查詢業主的賬務記錄,顯示業主編號、名稱、年、月、金額。如果賬務記錄沒有對應的業主信息,也要列出記錄。如下圖:
在這裏插入圖片描述
SQL1999標準的語句

select ow.id,ow.name,ac.year,ac.month,ac.money 
from T_OWNERS ow right join T_ACCOUNT ac
on ow.id=ac.owneruuid

Oracle的語法

select ow.id,ow.name,ac.year,ac.month,ac.money 
from T_OWNERS ow , T_ACCOUNT ac
where ow.id(+) =ac.owneruuid

2.3 子查詢

2.3.1 where子句中的子查詢

2.3.1.1 單行子查詢

需求:查詢2012年1月用水量大於平均值的臺賬記錄

select * from T_ACCOUNT
where year='2012' and month='01' and usenum>
( select avg(usenum) from T_ACCOUNT where year='2012' and month='01' );

查詢結果:
在這裏插入圖片描述
平均值爲:
在這裏插入圖片描述

2.3.1.2 多行子查詢

需求:查詢地址含有“花園”的業主的信息

select * from T_OWNERS
where addressid in
( select id from t_address where name like '%花園%' );

需求:查詢地址不含有“花園”的業主的信息
語句:

select * from T_OWNERS
where addressid not in
( select id from t_address where name like '%花園%' );

2.3.2 from子句中的子查詢

from 子句的子查詢爲多行子查詢
需求:查詢顯示業主編號,業主名稱,業主類型名稱,條件爲業主類型爲”居民”,使用子查詢實現。
語句:

select * from
(select o.id 業主編號,o.name 業主名稱,ot.name 業主類型
    from T_OWNERS o,T_OWNERTYPE ot
    where o.ownertypeid=ot.id)
where 業主類型='居民';

2.3.3 select子句中的子查詢

select子句的子查詢必須爲單行子查詢
需求1:列出業主信息,包括ID,名稱,所屬地址。

select id,name,
(select name from t_address where id=addressid) addressname
from t_owners;

查詢結果如下:
在這裏插入圖片描述
需求2:列出業主信息,包括ID,名稱,所屬地址,所屬區域。

select id,name,
    (select name from t_address where id=addressid) addressname,
    (select 
        (select name from t_area where id=areaid) 
    from t_address 
    where id=addressid)adrename
from t_owners;

查詢結果如下:
在這裏插入圖片描述

2.4 分頁查詢

2.4.1 簡單分頁

需求:分頁查詢臺賬表T_ACCOUNT,每頁10條記錄

分析:在Oracle進行分頁查詢,需要用到僞列ROWNUM和嵌套查詢
首先顯示前10條記錄,語句如下:

select rownum,t.* from T_ACCOUNT t where rownum<=10;

顯示結果如下:
在這裏插入圖片描述
顯示第11條到第20條的記錄,語句:

--錯誤演示
select rownum,t.* from T_ACCOUNT t where rownum>10 and rownum<=20;

查詢結果:
在這裏插入圖片描述
結果沒有數據是因爲rownum是在查詢語句掃描每條記錄時產生的,所以不能使用“大於”符號,只能使用“小於”或“小於等於” 可以使用子查詢來實現

select * from
(select rownum r,t.* from T_ACCOUNT t where rownum<=20)
where r>10;

查詢結果如下:
在這裏插入圖片描述

2.4.2 基於排序的分頁

需求:分頁查詢臺賬表T_ACCOUNT,每頁10條記錄,按使用字數降序排序。
我們查詢第2頁數據,如果基於上邊的語句添加排序,語句如下:

--錯誤演示
select * from
(select rownum r,t.* from T_ACCOUNT t where rownum<=20 order by usenum desc)
where r>10;

查詢結果如下:
在這裏插入圖片描述
經過驗證,我們看到第2頁的結果應該是下列記錄,所以推斷剛纔的語句是錯誤的
在這裏插入圖片描述
我們可以先單獨執行嵌套查詢裏面的那句話

select rownum r,t.* from T_ACCOUNT t
where rownum<=20 order by usenum desc;

你會看到查詢結果如下:
在這裏插入圖片描述
你會發現排序後的R是亂的。這是因爲ROWNUM僞列的產生是在表記錄掃描是產生的,而排序是後進行的,排序時R已經產生了,所以排序後R是亂的。
只要再嵌套一層循環(一共三層),讓結果先排序,然後對排序後的結果再產生R,這樣就不會亂了。

select * from
    (select rownum r,t.* from
        (select * from T_ACCOUNT order by usenum desc) t
            where rownum<=20 )
where r>10;

結果如下:
在這裏插入圖片描述

2.5 單行函數

2.5.1 字符函數

函數 說明
ASCII 返回對應字符的十進制值
CHR 給出十進制返回字符
CONCAT 拼接兩個字符串,與||相同
INITCAT 將字符串的第一個字母變爲大寫
INSTR 找出某個字符串的位置
INSTRB 找出某個字符串的位置和字節數
LENGTH 以字符給出字符串的長度
LENGTHB 以字節給出字符串的長度
LOWER 將字符串轉換成小寫
LPAD 使用指定的字符在字符的左邊填充
LTRIM 在左邊裁剪掉指定的字符
RPAD 使用指定的字符在字符的右邊填充
RTRIM 在右邊裁剪掉指定的字符
REPLACE 執行字符串搜索和替換
SUBSTR 取字符串的子串
SUBSTRB 取字符串的子串(以字節)
SOUNDEX 返回一個同音字符串
TRANSLATE 執行字符串搜索和替換
TRIM 裁剪掉前面或後面的字符串
UPPER 將字符串變爲大寫

2.5.1.1 求字符串長度LENGTH

select length('ABCD') from dual;

顯示結果爲:
在這裏插入圖片描述

2.5.1.2 求字符串的子串SUBSTR

select substr('ABCD',2,2) from dual;

顯示結果爲:
在這裏插入圖片描述

2.5.1.3 字符串拼接CONCAT

select concat('ABC','D') from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.1.4 字符串拼接||

可以用||對字符串進行拼接,查詢結果同上。

select 'ABC'||'D' from dual;

2.5.2 數值函數

函數 說明
ABS(value) 絕對值
CEIL(value) 大於或等於value的最小整數
COS(value) 餘弦
COSH(value) 反餘弦
EXP(value) e的value次冪
FLOOR(value) 小於或等於value的最大整數
LN(value) value的自然對數
LOG(value) value的以10爲底的對數
MOD(value,divisor) 求模
POWER(value,exponent) value的exponent次冪
ROUND(value,precision) 按precision精度4舍5入
SIGN(value) value爲正返回1;爲負返回-1;爲0返回0.
SIN(value) 餘弦
SINH(value) 反餘弦
SQRT(value) value的平方根
TAN(value) 正切
TANH(value) 反正切
TRUNC(value,按precision) 按照precision截取value
VSIZE(value) 返回value在Oracle的存儲空間大小

常用數值函數講解:

2.5.2.1 四捨五入函數ROUND

select round(100.567) from dual;

查詢結果如下:
在這裏插入圖片描述
語句:

select round(100.567,2) from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.2.2 截取函數TRUNC

select trunc(100.567) from dual;

查詢結果:
在這裏插入圖片描述
語句:

select trunc(100.567,2) from dual;

查詢結果:
在這裏插入圖片描述

2.5.2.3 取模MOD

select mod(10,3) from dual;

結果:
在這裏插入圖片描述

2.5.3 日期函數

函數 描述
ADD_MONTHS 在日期date上增加count個月
GREATEST(date1,date2,…) 從日期列表中選出最晚的日期
LAST_DAY(date) 返回日期date所在月的最後一天
LEAST(date1,date2,…) 從日期列表中選出最早的日期
MONTHS_BETWEEN(date2,date1) 給出Date2-date1的月數(可以是小數)
NEXT_DAY(date,’day’) 給出日期date之後下一天的日期,這裏的day爲星期,如:MONDAY,Tuesday等
NEW_TIME(date,’this’,’other’) 給出在this時區=Other時區的日期和時間
ROUND(date,’format’) 未指定format時,如果日期中的時間在中午之前,則將日期中的時間截斷爲12A.M.(午夜,一天的開始),否則進到第二天。時間截斷爲12A.M.(午夜,一天的開始),否則進到第二天
TRUNC(date,’format’) 未指定format時,將日期截爲12A.M.(午夜,一天的開始)

用 sysdate 這個系統變量來獲取當前日期和時間

select sysdate from dual;

常用日期函數講解:

2.5.3.1 加月函數ADD_MONTHS:在當前日期基礎上加指定的月

select add_months(sysdate,2) from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.3.2 求所在月最後一天LAST_DAY

語句:

select last_day(sysdate) from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.3.3 日期截取TRUNC

語句

select TRUNC(sysdate) from dual;

查詢結果如下:
在這裏插入圖片描述
語句:

select TRUNC(sysdate,'yyyy') from dual;

查詢結果如下:
在這裏插入圖片描述
語句:

select TRUNC(sysdate,'mm') from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.4 轉換函數

函數 描述
CHARTOROWID 將字符轉換到rowid類型
CONVERT 轉換一個字符節到另外一個字符節
HEXTORAW 轉換十六進制到raw類型
RAWTOHEX 轉換raw到十六進制
ROWIDTOCHAR 轉換ROWID到字符
TO_CHAR 轉換日期格式到字符串
TO_DATE 按照指定的格式將字符串轉換到日期型
TO_MULTIBYTE 把單字節字符轉換到多字節
TO_NUMBER 將數字字串轉換到數字
TO_SINGLE_BYTE 轉換多字節到單字節

常用轉換函數講解:

2.5.4.1 數字轉字符串TO_CHAR

語句:

select TO_CHAR(1024) from dual;

查詢結果:
在這裏插入圖片描述

2.5.4.2 日期轉字符串TO_CHAR

語句

select TO_CHAR(sysdate,'yyyy-mm-dd') from dual;

查詢結果:
在這裏插入圖片描述
語句:

select TO_CHAR(sysdate,'yyyy-mm-dd hh:mi:ss') from dual;

查詢結果:
在這裏插入圖片描述

2.5.4.3 字符串轉日期TO_DATE

語句:

select TO_DATE('2017-01-01','yyyy-mm-dd') from dual;

查詢結果如下:
在這裏插入圖片描述

2.5.4.4 字符串轉數字TO_NUMBER

select to_number('100') from dual;

2.5.5 其他函數

2.5.5.1 空值處理函數NVL

用法:
NVL(檢測的值,如果爲null的值)

select NVL(NULL,0) from dual;

查詢結果如下:
在這裏插入圖片描述
需求:顯示價格表中業主類型ID爲1的價格記錄,如果上限值爲 NULL,則顯示9999999

select PRICE,MINNUM,NVL(MAXNUM,9999999)
from T_PRICETABLE where OWNERTYPEID=1;

查詢結果:
在這裏插入圖片描述

2.5.5.2 空值處理函數NVL2

用法:NVL2(檢測的值,如果不爲null的值,如果爲null的值);
需求:顯示價格表中業主類型ID爲1的價格記錄,如果上限值爲NULL,顯示“不限”
在這裏插入圖片描述
語句:

select PRICE,MINNUM,NVL2(MAXNUM,to_char(MAXNUM) , '不限')
from T_PRICETABLE where OWNERTYPEID=1;

2.5.5.3 條件取值 decode

語法:decode(條件,值1,翻譯值1,值2,翻譯值2,…值n,翻譯值n,缺省值)
——根據條件返回相應值
需求:顯示下列信息(不要關聯查詢業主類型表,直接判斷 1 2 3 的值)
在這裏插入圖片描述
語句:

select name,decode( ownertypeid,1,'居民',2,'行政事業單位',3,'商業') as 類型 from T_OWNERS;

上邊的語句也可以用 case when then 語句來實現

select name ,(case ownertypeid
                when 1 then '居民'
                when 2 then '行政事業單位'
                when 3 then '商業'
                else '其它'
                end
) from T_OWNERS;

還有另外一種寫法:

select name,(case
            when ownertypeid= 1 then '居民'
            when ownertypeid= 2 then '行政事業'
            when ownertypeid= 3 then '商業'
            end ) 
from T_OWNERS;

2.6 行列轉換

需求:按月份統計2012年各個地區的水費,如下圖
在這裏插入圖片描述

select (select name from T_AREA where id= areaid ) 區域,
    sum( case when month='01' then money else 0 end) 一月,
    sum( case when month='02' then money else 0 end) 二月,
    sum( case when month='03' then money else 0 end) 三月,
    sum( case when month='04' then money else 0 end) 四月,
    sum( case when month='05' then money else 0 end) 五月,
    sum( case when month='06' then money else 0 end) 六月,
    sum( case when month='07' then money else 0 end) 七月,
    sum( case when month='08' then money else 0 end) 八月,
    sum( case when month='09' then money else 0 end) 九月,
    sum( case when month='10' then money else 0 end) 十月,
    sum( case when month='11' then money else 0 end) 十一月,
    sum( case when month='12' then money else 0 end) 十二月
from T_ACCOUNT where year='2012' group by areaid;

需求:按季度統計2012年各個地區的水費,如下圖
在這裏插入圖片描述
語句如下:

select (select name from T_AREA where id= areaid ) 區域,
sum( case when month>='01' and month<='03' then money else 0 end) 第一季度,
sum( case when month>='04' and month<='06' then money else 0 end) 第二季度,
sum( case when month>='07' and month<='09' then money else 0 end) 第三季度,
sum( case when month>='10' and month<='12' then money else 0 end) 第四季度
from T_ACCOUNT where year='2012' group by areaid;

2.7 分析函數

以下三個分析函數可以用於排名使用。
下圖爲三種排名方式的舉例
在這裏插入圖片描述

2.7.1 RANK相同的值排名相同,排名跳躍

需求:對T_ACCOUNT表的usenum 字段進行排序,相同的值排名相同,排名跳躍
語句:

select rank() over(order by usenum desc ),usenum from T_ACCOUNT;

結果:
在這裏插入圖片描述

2.7.2 DENSE_RANK相同的值排名相同,排名連續

需求:對T_ACCOUNT表的usenum字段進行排序,相同的值排名相同,排名連續
語句:

select dense_rank() over(order by usenum desc ),usenum from T_ACCOUNT;

結果:
在這裏插入圖片描述

2.7.3 ROW_NUMBER返回連續的排名,無論值是否相等

需求:對T_ACCOUNT表的usenum字段進行排序,返回連續的排名,無論值是否相等
語句:

select row_number() over(order by usenum desc),usenum from T_ACCOUNT;

結果:
在這裏插入圖片描述
用row_number()分析函數實現的分頁查詢相對三層嵌套子查詢要簡單的多:

select * from
(select row_number() over(order by usenum desc )
rownumber,usenum from T_ACCOUNT)
where rownumber>10 and rownumber<=20;

查詢結果如下:
在這裏插入圖片描述

2.8 集合運算

2.8.1 什麼是集合運算

集合運算,集合運算就是將兩個或者多個結果集組合成爲一個結果集。集合運算包括:
UNION ALL(並集),返回各個查詢的所有記錄,包括重複記錄。
UNION(並集),返回各個查詢的所有記錄,不包括重複記錄。
INTERSECT(交集),返回兩個查詢共有的記錄。
MINUS(差集),返回第一個查詢檢索出的記錄減去第二個查詢檢索出的記錄之後剩餘的記錄。
在這裏插入圖片描述

2.8.2 並集運算

UNION ALL不去掉重複記錄

select * from t_owners where id<=7
union all
select * from t_owners where id>=5;

結果如下:
在這裏插入圖片描述
UNION去掉重複記錄

select * from t_owners where id<=7
union
select * from t_owners where id>=5;

結果如下:
在這裏插入圖片描述

2.8.3 交集運算

select * from t_owners where id<=7
intersect
select * from t_owners where id>=5;

結果:
在這裏插入圖片描述

2.8.4 差集運算

select * from t_owners where id<=7
minus
select * from t_owners where id>=5;

結果:
在這裏插入圖片描述
如果我們用minus運算符來實現分頁,語句如下:

select rownum,t.* from T_ACCOUNT t where rownum<=20
minus
select rownum,t.* from T_ACCOUNT t where rownum<=10;

結果:
在這裏插入圖片描述

2.9 使用system賬號給scott解鎖/改密碼:

解鎖:

alter user scott account unlock;

改密碼:

alter user scott identified by tiger;  --scott的默認密碼是tiger

3 Oracle對象

3.1 視圖

3.1.1 什麼是視圖

視圖是一種數據庫對象,是從一個或者多個數據表或視圖中導出的虛表,視圖所對應的數據並不真正地存儲在視圖中,而是存儲在所引用的數據表中,視圖的結構和數據是對數據表進行查詢的結果。根據創建視圖時給定的條件,視圖可以是一個數據表的一部分,也可以是多個基表的聯合,它存儲了要執行檢索的查詢語句的定義,以便在引用該視圖時使用。
使用視圖的優點:

  • 簡化數據操作:視圖可以簡化用戶處理數據的方式。
  • 着重於特定數據:不必要的數據或敏感數據可以不出現在視圖中。
  • 視圖提供了一個簡單而有效的安全機制,可以定製不同用戶對數據的訪問權限。
  • 提供向後兼容性:視圖使用戶能夠在表的架構更改時爲表創建向後兼容接口。

3.1.2 創建或修改視圖語法

CREATE [OR REPLACE] [FORCE] VIEW view_name
AS subquery
[WITH CHECK OPTION ]
[WITH READ ONLY];

選項解釋:
OR REPLACE:若所創建的試圖已經存在ORACLE自動重建該視圖;
FORCE:不管基表是否存在ORACLE都會自動創建該視圖;
subquery:一條完整的SELECT語句,可以在該語句中定義別名;
WITH CHECK OPTION :插入或修改的數據行必須滿足視圖定義的約束;
WITH READ ONLY :該視圖上不能進行任何DML操作。

3.1.3 刪除視圖語法

DROP VIEW view_name;

3.1.4 示例

3.1.4.1 簡單視圖的創建與使用

簡單視圖:如果視圖中的語句只是單表查詢,並且沒有聚合函數,我們就稱之爲簡單視圖。
需求:創建視圖:業主類型爲 1 的業主信息
語句:

create or replace view view_owners1 as
select * from T_OWNERS where ownertypeid= 1;

利用該視圖進行查詢

select * from view_owners1 where addressid= 1;

就像使用表一樣去使用視圖就可以了。
對於簡單視圖,可以用查詢,還可以增刪改記錄。
執行下面寫一條更新的語句:

update view_owners1 set name 王剛 where id 2;

再次查詢:

select * from view_owners1;

查詢結果如下:
在這裏插入圖片描述
結果已經更改成功。再次查詢表數據
在這裏插入圖片描述
發現表的數據也跟着更改了。由此得出結論:視圖其實是一個虛擬的表,它的數據其實來自於表。如果更改了視圖的數據,表的數據也自然會變化,更改了表的數據,視圖也自然會變化。一個視圖所存儲的並不是數據,而是一條SQL語句。

3.1.4.2 帶檢查約束的視圖

需求:根據地址表T_ADDRESS創建視圖VIEW_ADDRESS2 , 內容爲區域ID爲2的記錄。
語句:

create or replace view view_address2 as
select * from T_ADDRESS where areaid= 2
with check option;

執行下列更新語句:

update view_address2 set areaid=11 where id=44;

系統提示如下錯誤信息:
在這裏插入圖片描述

3.1.4.3 只讀視圖的創建與使用

需求:將上邊的視圖修改爲只讀視圖
語句:

create or replace view view_owners1 as
select * from T_OWNERS where ownertypeid= 1
with read only;

修改後,再次執行update 語句,會出現如下錯誤提示
在這裏插入圖片描述

3.1.4.4 創建帶錯誤的視圖

創建一個視圖,如果視圖的SQL語句所設計的表並不存在,如下:

create or replace view view_TEMP as
select * from T_TEMP;

T_TEMP表並不存在,此時系統會給出錯誤提示表並不存在,此時系統會給出錯誤提示
在這裏插入圖片描述
有的時候,我們創建視圖時的表可能並不存在,但是以後可能會存在,我們如果此時需要創建這樣的視圖,需要添加FORCE選項,SQL語句如下:

create or replace FORCE view view_TEMP as
select * from T_TEMP;

此時視圖創建成功。

3.1.4.5 複雜視圖的創建與使用

複雜視圖,就是視圖的SQL語句中,有聚合函數或多表關聯查詢。
示例:

3.1.4.5.1 多表關聯查詢示例:

需求:創建視圖,查詢顯示業主編號業主名稱,業主類型名稱
語句:

create or replace view view_owners as
select o.id 業主編號 ,o.name 業主名稱 ,ot.name 業主類型
from T_OWNERS o,T_OWNERTYPE ot
where o.ownertypeid=ot.id;

試一下下面的語句:

update view_owners set 業主名稱 範小冰 where 業主編號 1;

可以修改成功。再試一下下面的語句:

update view_owners set 業主類型 普通居民 where 業主編號 1;

系統彈出錯誤提示:
在這裏插入圖片描述
是說所需改的列不屬於鍵保留表的列。
鍵保留表是理解連接視圖修改限制的一個基本概念。該表的主鍵列全部顯示在視圖中並且它們的值在視圖中都是唯一且非空的。也就是說,表的鍵值在一個連接視圖中也是鍵值,那麼就稱這個表爲鍵保留表。
在這個例子中,視圖中存在兩個表,業主表 T_OWNERS和業主類型表T_OWNERTYPE, 其中 T_OWNERS 表就是鍵保留表,因爲T_OWNERS的主鍵也是作爲視圖的主鍵。鍵保留表的字段是可以更新的,而非鍵保留表是不能更新的

3.1.4.5.2 分組聚合統計查詢的例子

需求:創建視圖,按年月統計水費金額,效果如下:

語句:

create view view_accountsum as
select year month sum (money) moneysum
from T_ACCOUNT
group by year month
order by year month;

此例用到聚合函數,沒有鍵保留表,所以無法執行update 。

3.2 物化視圖

3.2.1 什麼是物化視圖

視圖是一個虛擬表(也可以認爲是一條語句),基於它創建時指定的查詢語句返回的結果集。每次訪問它都會導致這個查詢語句被執行一次。爲了避免每次訪問都執行這個查詢,可以將這個查詢結果集存儲到一個物化視圖(也叫實體化視圖)。
物化視圖與普通的視圖相比的區別是物化視圖是建立的副本,它類似於一張表,需要佔用存儲空間。而對一個物化視圖查詢的執行效率與查詢一個表是一樣的。

3.2.2 創建物化視圖語法

CREATE METERIALIZED VIEW view_name
[BUILD IMMEDIATE | BUILD DEFERRED]
REFRESH [FAST | COMPLETE | FORCE]
[
ON [COMMIT | DEMAND] | START WITH (start_time) NEXT (next_time)
]
AS
subquery
  • BUILD IMMEDIATE是在創建物化視圖的時候就生成數據
  • BUILD DEFERRED則在創建時不生成數據, 以後根據需要再生成數據。默認爲BUILD IMMEDIATE。
  • 刷新REFRESH:指當基表發生了DML操作後,物化視圖何時採用哪種方式和基表進行同步。

REFRESH後跟着指定的刷新方法有三種:FAST、COMPLETE、FORCE。FAST刷新採用增量刷新,只刷新自上次刷新以後進行的修改。 COMPLETE刷新對整個物化視圖進行完全的刷新。如果選擇FORCE方式,則Oracle在刷新時會去判斷是否可以進行快速刷新,如果可以則採用FAST方式,否則採用COMPLETE的方式。FORCE是默認的方式。

  • 刷新的模式有兩種:ON DEMAND和ON COMMIT。ON DEMAND指需要手動刷新物化視圖(默認)。ON COMMIT 指在基表發生COMMIT操作時自動刷新。

3.2.3 示例

3.2.3.1 創建手動刷新的物化視圖

需求:查詢地址ID, 地址名稱和所屬區域名稱 , 結果如下:
在這裏插入圖片描述
語句:

create materialized view mv_address
as
select ad.id,ad.name adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id

執行上邊的語句後查詢

select * from mv_address;

查詢結果如下:
在這裏插入圖片描述

這時,向地址表(T_ADDRESS )中插入一條新記錄

insert into t_address values(8,'宏福苑小區',1,1);

再次執行上邊的語句進行查詢,會發現新插入的語句並沒有出現在物化視圖中。
需要通過下面的語句(PL/SQL ),手動刷新物化視圖

begin 
DBMS_MVIEW.refresh('MV_ADDRESS','C');
end;

或者通過下面的命令手動刷新物化視圖:

EXEC DBMS_MVIEW.refresh('MV_ADDRESS','C');
--注意:此語句需要在命令窗口中執行

執行此命令後再次查詢物化視圖,就可以查詢到最新的數據了。
DBMS_MVIEW.refresh實際上是系統內置的存儲過程,關於存儲過程在第4章會詳細講解。

3.2.3.2 創建自動刷新的物化視圖

和上例一樣的結果集
語句如下:

create materialized view mv_address2
refresh
on commit 
as
select ad.id,ad.name adname,ar.name ar_name 
from t_address ad,t_area ar
where ad.areaid=ar.id

創建此物化視圖後,當T_ADDRESS表發生變化時,MV_ADDRESS2自動跟着改變。

3.2.3.3 創建時不生成數據的物化視圖

create materialized view mv_address3
build deferred
refresh
on commit
as
select ad.id,ad.name adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id;

創建後執行下列語句查詢物化視圖

select * from mv_address3

查詢結果:
在這裏插入圖片描述
執行下列語句生成數據

begin
DBMS_MVIEW.refresh('MV_ADDRESS3','C');
end;

再次查詢,得到結果:
在這裏插入圖片描述
由於創建時指定的on commit所以在修改數據後能立刻看到最新數據,無須再次執行refresh

3.2.3.4 創建增量刷新的物化視圖

如果創建增量刷新的物化視圖,必須首先創建物化視圖日誌

create materialized view log on t_address with rowid
create materialized view log on t_area with rowid

創建的物化視圖日誌名稱爲MLOG$_ 表名稱
創建物化視圖

create materialized view mv_address4
refresh fast
as
selec t ad.rowid adrowid ,ar.rowid arrowid, ad.id,ad.name
adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id;

注意:創建增量刷新的物化視圖,必須:
1.創建物化視圖中涉及表的物化視圖日誌。
2.在查詢語句中,必須包含所有表的rowid (以rowid方式建立物化視圖日誌)
當我們向地址表插入數據後,物化視圖日誌的內容:
在這裏插入圖片描述
SNAPTIME$$:用於表示刷新時間。
DMLTYPE$$:用於表示DML操作類型,I表示INSERT,D表示DELETE,U表示UPDATE 。
OLD_NEW$$ NEW$$:用於表示這個值是新值還是舊值。 N(EW)表示新值O(LD)表示舊值,U表示UPDATE 操作。
CHANGE_VECTOR$$ VECTOR$$:表示修改矢量,用來表示被修改的是哪個或哪幾個字段。此列是 RAW 類型,其實Oracle採用的方式就是用每個BIT位去映射一個列。
插入操作顯示爲FE,刪除顯示爲OO,更新操作則根據更新字段的位置而顯示不同的值。
當我們手動刷新物化視圖後,物化視圖日誌被清空,物化視圖更新。

begin
DBMS_MVIEW.refresh('MV_ADDRESS4','C');
end;

3.3 序列

3.3.1 什麼是序列

序列是Oracle提供的用於產生一系列唯一數字的數據庫對象 。

3.3.2 創建與使用簡單序列

創建序列語法:

create sequence 序列名稱

通過序列的僞列來訪問序列的值

  • NEXTVAL返回序列的下一個值
  • CURRVAL返回序列的當前值

注意:在剛建立序列後,無法提取當前值,只有先提取下一個值時才能再次提取當前值。

--提取下一個值
select 序列名稱.nextval from dual
--提取當前值
select 序列名稱.currval from dual

3.3.3 創建複雜序列

語法:

CREATE SEQUENCE sequence //創建序列名稱
[INCREMENT BY n] //遞增的序列值是n 如果n是正數就遞增,如果是負數就遞減 默認是1
[START WITH n] //開始的值,遞增默認是minvalue 遞減是maxvalue
[{MAXVALUE n | NOMAXVALUE}] //最大值
[{MINVALUE n | NOMINVALUE}] //最小值
[{CYCLE | NOCYCLE}] //循環/不循環
[{CACHE n | NOCACHE}];//分配並存入到內存中

3.3.4 示例

3.3.4.1 有最大值的非循環序列

創建序列的語句:

create sequence seq_test1
increment by 10
start with 10
maxvalue 300
minvalue 20

結果:
在這裏插入圖片描述
以上的錯誤,是由於開始值小於最小值。開始值不能小於最小值,修改以上語句:

create sequence seq_test1
increment by 10
start with 10
maxvalue 300
minvalue 5

執行該語句提取序列值,當序列值爲 300 (最大值)的時候再次提取值系統會報異常信息。
在這裏插入圖片描述

3.3.4.2 有最大值的循環序列

create sequence seq_test2
increment by 10
start with 10
maxvalue 300
minvalue 5
cycle;

當序列當前值爲300 (最大值),再次提取序列的值

select seq_test2.nextval from dual

提取的值爲:
在這裏插入圖片描述

由此得出結論,循環的序列,第一次循環是從開始值開始循環,而第二次循環是從最小值開始循環。
思考問題:下列語句是否會報錯?爲什麼?

create sequence seq_test3
increment by 10
start with 10
minvalue 5
cycle;

答:此爲錯誤的語句。因爲你創建的是一個循環的序列,所以必須指定最大值,否則會報錯。

3.3.4.3 帶緩存的序列

執行下列語句:

create sequence seq_test3
increment by 10
start with 10
maxvalue 300
minvalue 5
cycle
cache 50;

執行上邊語句的意思是每次取出50個緩存值,但是執行會提示錯誤
在這裏插入圖片描述
上邊錯誤提示的意思是:緩存設置的數必須小於每次循環的數。
緩存設定的值是50 ,而最大值是300,爲什麼還會提示這樣的信息呢?
其實cache雖然是50 ,但是每次增長值是10。這樣50次緩存提取出的數是500(50*10)
更改爲下列的語句:

create sequence seq_test4
increment by 10
start with 10
maxvalue 500
min value 10
cycle
cache 50;

下列語句依然會提示上邊的錯誤,這是因爲還存在一個minvalue,minvalue 和maxvalue之間是490個數,也就是一次循環可以提取490,但是緩存是500 。
再次修改語句:

create sequence seq_test5
increment by 10
start with 10
maxvalue 500
minvalue 9
cycle
cache 50;

把最小值減1,或把最大值加1,都可以通過。

3.3.5 修改和刪除序列

修改序列:使用ALTER SEQUENCE語句修改序列,不能更改序列的START WITH參數

ALTER SEQUENCE 序列名稱 MAXVALUE 5000 CYCLE;

刪除序列:

DROP SEQUENCE 序列名稱

3.4 同義詞

3.4.1 什麼是同義詞

同義詞實質上是指定方案對象的一個別名。通過屏蔽對象的名稱和所有者以及對分佈式數據庫的遠程對象提供位置透明性,同義詞可以提供一定程度的安全性。同時,同義詞的易用性較好,降低了數據庫用戶的SQL 語句複雜度。同義詞允許基對象重命名或者移動,這時,只需對同義詞進行重定義,基於同義詞的應用程序可以繼續運行而無需修改。
你可以創建公共同義詞和私有同義詞。其中,公共同義詞屬於PUBLIC特殊用戶組,數據庫的所有用戶都能訪問;而私有同義詞包含在特定用戶的方案中,只允許特定用戶或者有基對象訪問權限的用戶進行訪問。
同義詞本身不涉及安全,當你賦予一個同義詞對象權限時,你實質上是在給同義詞的基對象賦予權限,同義詞只是基對象的一個別名。

3.4.2 創建與使用同義詞

創建同義詞的具體語法是:

create [public] SYNONYM synooym for object;

synonym表示要創建的同義詞的名稱,object 表示表,視圖 ,序列等要創建同義詞的對象的名稱。

3.4.3 示例

3.4.3.1 私有同義詞

需求:爲表T_OWNERS 創建私有同義詞名稱爲OWNERS
語句:

create synonym OWNERS for T_OWNERS;

使用同義詞:

select * from OWNERS;

查詢結果如下:
在這裏插入圖片描述

3.4.3.2 公有同義詞

需求:爲表T_OWNERS創建公有同義詞名稱爲OWNERS2

create public synonym OWNERS 2 for T_OWNERS;

以另外的用戶登陸,也可以使用公有同義詞:

select * from OWNERS 2

3.5 索引

3.5.1 什麼是索引

索引是用於加速數據存取的數據對象。合理的使用索引可以大大降低i/o次數從而提高數據訪問性能 。
索引是需要佔據存儲空間的,也可以理解爲是一種特殊的數據。形式類似於下圖的一棵“樹”,而樹的節點存儲的就是每條記錄的物理地址,也就是僞列ROWID
在這裏插入圖片描述

3.5.2 普通索引

語法:

create index 索引名稱 on 表名(列名);

需求:
根據業主名稱搜索業主信息,所以基於業主表的name字段來建立索引。語句如下:

create index index_owners_name on T_OWNERS(name)

索引性能測試:
創建一個兩個字段的表

creat e table T_INDEXTEST (
ID NUMBER,
NAME VARCHAR2(30)
);

編寫PL/SQL插入100萬條記錄(關於PL/SQL在第4節)

BEGIN
    FOR i in 1 ..1000000
    loop
        INSERT INTO T_INDEXTEST VALUES ( i,'AA'||i);
    end loop;
    commit;
END;

創建完數據後,根據name列創建索引

CREATE INDEX INDEX_TESTINDEX o n T_INDEXTEST(name)

執行下面兩句SQL執行

SELECT * from T_INDEXTEST where ID 765432;
SELECT * from T_INDEXTEST where NAME ='AA765432';

發現根據name查詢所用的時間會比根據id查詢所用的時間要短

3.5.3 唯一索引

如果需要在某個表某個列創建索引,而這列的值是不會重複的。這時可以創建唯一索引。
語法:

create unique index 索引名稱 on 表名(列名);

需求:在業主表的水錶編號一列創建唯一索引
語句:

create unique index index_owners_watermeter on T_OWNERS(watermeter);

3.5.4 複合索引

如果經常要對某幾列進行查詢,比如,經常要根據學歷和性別對學員進行搜索,如果對這兩列建立兩個索引,因爲要查兩棵樹,查詢性能不一定高。可以建立複合索引,也就是基於兩個以上的列建立一個索引 。
語法:

create index 索引名稱 on 表名(列名,列名......);

根據地址和門牌號對學員表創建索引,語句如下:

create index owners_index_ah
on T_OWNERS(addressid,housenumber);--字段順序要與查詢時的一致

3.5.5 反向鍵索引

應用場景:當某個字段的值爲連續增長的值,如果構建標準索引,會形成歪脖子樹。這樣會增加查詢的層數,性能會下降。建立反向鍵索引,可以使索引的值變得不規則,從而使索引樹能夠均勻分佈。
在這裏插入圖片描述
語法:

create index 索引名稱 on 表名(列名) reverse;

3.5.6 位圖索引

使用場景:位圖索引適合創建在低基數列上位圖索引不直接存儲ROWID ,而是存儲字節位到 ROWID 的映射
優點:減少響應時間,節省空間佔用,語法:

create bitmap index 索引名稱 on 表名(列名);

需求:在T_owners表的ownertypeid列上建立位圖索引,語句:

create bitmap index index_owners_typeid
on T_OWNERS(ownertypeid)

4 Oracle編程

4.1 PL/SQL

4.1.1 什麼是PL/SQL

PL/SQL(ProcedureLanguage/SQL)是Oracle對sql語言的過程化擴展,指在SQL命令語言中增加了過程處理語句(如分支、循環等),使SQL語言具有過程處理能力。把SQL語言的數據操縱能力與過程語言的數據處理能力結合起來,使得PLSQL面向過程但比過程語言簡單、高效、靈活和實用。
基本語法結構

[declare
    --聲明變量
    ]
 begin
        --代碼邏輯
    [exception
        --異常處理
        ]
end;

4.1.2 變量

聲明變量的語法:

變量名 類型 (長度)

變量賦值的語法:

變量名:=變量值

需求:聲明變量水費單價、水費字數、噸數、金額。對水費單價、字數、進行賦值。噸數根據水費字數換算,規則爲水費字數除以1000 ,並且四捨五入,保留兩位小數。計算金額,金額 單價 噸數。
輸出單價、數量和金額。
–變量的用法–

declare
    v_price number(10,2); --水費單價
    v_usenum number; --水費字數
    v_usenum2 number(10,2); --噸數
    v_money number(10,2); --金額
begin
    v_price:=2.45; --水費單價
    v_usenum:=8012; --字數
    --字數換算爲噸數
    v_usenum2:=round(v_usenum/1000,2);
    --計算金額
    v_money:=round(v_price*v_usenum2,2);
    dbms_output.put_line('單價:'||v_price||'噸數:'||v_usenum2||'金額:'||v_money);
end;

select into方式賦值
語法結構:

select 列名 into 變量名 from 表名 where 條件

注意:結果必須是一條記錄,有多條記錄和沒有記錄都會報錯

declare
v_price number(10,2);--單價
v_usenum number;--水費字數
v_num0 number;--上月字數
v_num1 number;--本月字數
v_usenum2 number(10,2);--使用噸數
v_money number(10,2);--水費金額
begin
--對單價進行賦值
v_price:=3.45;
--變量賦值
select usenum,num0,num1 into v_usenum,V_num0,V_num1 from T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;

v_usenum2:= round(v_usenum/1000,2); 
v_money:=v_price*v_usenum2;
DBMS_OUTPUT.put_line('單價:'||v_price||'噸數:'
||v_usenum2||'金額:'||v_money||'上月字數:'||v_num0||'本月  字數'||v_num1);
end;

4.1.3 屬性類型

%TYPE 引用型
作用:引用某表某列的字段類型

declare
v_price number(10,2);--單價
v_usenum T_ACCOUNT.USENUM%TYPE;--水費字數
v_num0 T_ACCOUNT.NUM0%TYPE;--上月字數
v_num1 T_ACCOUNT.NUM1%TYPE;--本月字數
v_usenum2 number(10,2);--使用噸數
v_money number(10,2);--水費金額begin
--對單價進行賦值
v_price:=3.45;
--v_usenum:=8090;
select usenum,num0,num1 into v_usenum,V_num0,V_num1 from T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;
--使用噸數
v_usenum2:= round(v_usenum/1000,2);
--計算金額
v_money:=v_price*v_usenum2; DBMS_OUTPUT.put_line('單價:'||v_price||'噸數:'
||v_usenum2||'金額:'||v_money||'上月字數:'||v_num0||'本月字數'||v_num1); 
end;

%ROWTYPE 記錄型 ,上例中的例子可以用下面的代碼代替
作用: 標識某個表的行記錄類型

declare
v_price number(10,2);--單價
v_account T_ACCOUNT%ROWTYPE;--記錄型
v_usenum2 number(10,2);--使用噸數
v_money number(10,2);--水費金額
begin
--對單價進行賦值
v_price:=3.45;
--賦值
select * into v_account from T_ACCOUNT where year='2012' and month='01' and owneruuid=1;
--使用噸數
v_usenum2:= round(v_account.usenum/1000,2);
--計算金額
v_money:=v_price*v_usenum2;
DBMS_OUTPUT.put_line('單價:'||v_price||'噸數:'
||v_usenum2||'金額:'||v_money||'上月字數:'||v_account.num0||'本月字數'||v_account.num1); 
end;

4.1.4 異常

在運行程序時出現的錯誤叫做異常
發生異常後,語句將停止執行,控制權轉移到PL/SQL塊的異常處理部分
異常有兩種類型:

  • 預定義異常-當PL/SQL程序違反Oracle規則或超越系統限制時隱式引發
  • 用戶定義異常-用戶可以在PL/SQL塊的聲明部分定義異常,自定義的異常通過RAISE語句顯式引發
    預定義異常

Oracle 預定義異常 21 個

命名的系統異常 產生原因
ACCESS_INTO_NULL 未定義對象
CASE_NOT_FOUND CASE中若未包含相應的WHEN,並且沒有設置ELSE時
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 遊標已經打開
DUP_VAL_ON_INDEX 唯一索引對應的列上有重複的值
INVALID_CURSOR 在不合法的遊標上進行操作
INVALID_NUMBER 內嵌的SQL語句不能將字符轉換爲數字
NO_DATA_FOUND 使用selectinto未返回行
TOO_MANY_ROWS 執行selectinto時,結果集超過一行
ZERO_DIVIDE 除數爲0
SUBSCRIPT_BEYOND_COUNT 元素下標超過嵌套表或VARRAY的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或VARRAY時,將下標指定爲負數
VALUE_ERROR 賦值時,變量長度不足以容納實際數據
LOGIN_DENIED PL/SQL應用程序連接到oracle數據庫時,提供了不正確的用戶名或密碼
NOT_LOGGED_ON PL/SQL應用程序在沒有連接oralce數據庫的情況下訪問數據
PROGRAM_ERROR PL/SQL內部問題,可能需要重裝數據字典&pl./SQL系統包
ROWTYPE_MISMATCH 宿主遊標變量與PL/SQL遊標變量的返回類型不兼容
SELF_IS_NULL 使用對象類型時,在null對象上調用對象方法
STORAGE_ERROR 運行PL/SQL時,超出內存空間
SYS_INVALID_ID 無效的ROWID字符串
TIMEOUT_ON_RESOURCE Oracle在等待資源時超時

語法結構:

exception
when 異常類型 then
異常處理邏輯

根據上例中的代碼,添加異常處理部分:

declare
v_price number(10,2);--水費單價
v_usenum T_ACCOUNT.USENUM%type; --水費字數
v_usenum2 number(10,3);--噸數
v_money number(10,2);--金額
begin
v_price:=2.45;--水費單價
select usenum into v_usenum from T_ACCOUNT where owneruuid=1 and year='2012' and month='01';
--字數換算爲噸數
v_usenum2:= round( v_usenum/1000,3);
--計算金額
v_money:=round(v_price*v_usenum2,2); 
dbms_output.put_line('單價:'||v_price||'噸數:'||v_usenum2||'金額:'||v_money);
exception
    when NO_DATA_FOUND then
        dbms_output.put_line('未找到數據,請覈實');
    when TOO_MANY_ROWS then
        dbms_output.put_line('查詢條件有誤,返回多條信息,請覈實');
end;

基本語法1:

if 條件 then 
    業務邏輯
end if;

基本語法2:

if 條件 then 
    業務邏輯 
else 
    業務邏輯 
end if;

基本語法3:

if 條件 then 
    業務邏輯 
elsif 條件 then 
    業務邏輯 
else 
    業務邏輯 
end if;

需求:設置三個等級的水費5噸以下2.45元/噸5噸到10噸部分3.45元/噸,超過10噸部分4.45,根據使用水費的量來計算階梯水費。

declare
v_price1 number(10,2);--不足5噸的單價
v_price2 number(10,2);--超過5噸不足10噸單價
v_price3 number(10,2);--超過10噸單價
v_account T_ACCOUNT%ROWTYPE;--記錄型
v_usenum2 number(10,2);--使用噸數v_money number(10,2);--水費金額
begin
--對單價進行賦值
v_price1:=2.45;
v_price2:=3.45;
v_price3:=4.45;

--賦值
select * into v_account from T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;
--使用噸數
v_usenum2:= round(v_account.usenum/1000,2);

--計算金額(階梯水費)
if v_usenum2<=5 then--第一個階梯
    v_money:=v_price1*v_usenum2;
elsif  v_usenum2>5 and v_usenum2<=10 then --第二個階梯
    v_money:=v_price1*5 + v_price2*( v_usenum2-5);
else	--第三個階梯
    v_money:=v_price1*5 +v_price2*5 + v_price3*( v_usenum2-10 ); 
end if;

DBMS_OUTPUT.put_line('噸數:'||v_usenum2||'金額:'||v_money||'上月字數:'||v_account.num0
    ||'本月字數'||v_account.num1);

exception
    when NO_DATA_FOUND then 
        DBMS_OUTPUT.put_line('沒有找到數據');
    when TOO_MANY_ROWS then
        DBMS_OUTPUT.put_line('返回的數據有多行');
end;

4.1.6 循環

4.1.6.1 無條件循環

語法結構:

loop
--循環語句
end loop;

範例:輸出從1開始的100個數

declare
v_num number:=1;
begin
    loop
      dbms_output.put_line(v_num);
      v_num:=v_num+1;
    exit when v_num>100;
    end loop;
end;

4.1.6.2 條件循環

語法結構:

while 條件
loop
end loop;
declare
    v_num number:=1;
begin
    while v_num<=100
    loop
        dbms_output.put_line(v_num);
        v_num:=v_num+1;
    end loop;
end;

4.1.6.3 for循環

基本語法:

for 變量 in 起始值..終止值
loop
end loop;
begin
    for v_num in 1..100
    loop
        dbms_output.put_line(v_num);
    end loop;
end;

4.1.7 遊標

4.1.7.1 什麼是遊標

遊標是系統爲用戶開設的一個數據緩衝區,存放SQL語句的執行結果。我們可以把遊標理解爲PL/SQL中的結果集。
在這裏插入圖片描述

4.1.7.2 語法結構及示例

在聲明區聲明遊標,語法如下:

cursor遊標名稱 is SQL 語句;

使用遊標語法

open 遊標名稱
loop
fetch 遊標名稱 into 變量
exit when 遊標名稱%notfound 
end loop;
close 遊標名稱

需求:打印業主類型爲 1 的價格表代碼:

declare
    v_pricetable T_PRICETABLE%rowtype;--價格行對象
    cursor cur_pricetable is select * from T_PRICETABLE where ownertypeid=1;--定義遊標begin
open cur_pricetable;--打開遊標loop
    fetch cur_pricetable into v_pricetable;--提取遊標到變量
    exit when cur_pricetable%notfound;--當遊標到最後一行下面退出循環
    dbms_output.put_line('價格:'||v_pricetable.price ||'噸位:'||v_pricetable.minnum||'-'
            ||v_pricetable.maxnum);
end loop;
close cur_pricetable;--關閉遊標
end;

4.1.7.3 帶參數的遊標

declare
    v_pricetable T_PRICETABLE%rowtype;--價格行對象
    cursor cur_pricetable(v_ownertypeid number) is 
        select * from T_PRICETABLE where ownertypeid=v_ownertypeid;-- 定義遊標
begin
open cur_pricetable(2);--打開遊標loop
fetch cur_pricetable into v_pricetable;--提取遊標到變量
exit when cur_pricetable%notfound;--當遊標到最後一行下面退出循環
dbms_output.put_line('價格:'||v_pricetable.price ||'噸位 :'||v_pricetable.minnum||'-'
            ||v_pricetable.maxnum );
end loop;
close cur_pricetable;--關閉遊標
end;

4.1.7.4 for循環提取遊標值

declare
cursor cur_pricetable(v_ownertypeid number) is 
select * from T_PRICETABLE where ownertypeid=v_ownertypeid;--定義遊標
begin
for v_pricetable in cur_pricetable(3)
loop
dbms_output.put_line('價格:'||v_pricetable.price ||'噸位 :'||v_pricetable.minnum||'-'
                        ||v_pricetable.maxnum);
end loop;
end;

4.2 存儲函數

4.2.1 什麼是存儲函數

存儲函數又稱爲自定義函數。可以接收一個或多個參數,返回一個結果。在函數中我們可以使用PL/SQL進行邏輯的處理。

4.2.2 存儲函數語法結構

創建或修改存儲過程的語法如下:

CREATE[OR REPLACE] FUNCTION 函數名稱
(參數名稱 參數類型, 參數名稱 參數類型, ...RETURN 結果變量數據類型
IS
    變量聲明部分; 
BEGIN
    邏輯部分;
RETURN 結果變量;
 [EXCEPTION
    異常處理部分]
END;

4.2.3 示例

需求:創建存儲函數,根據地址ID查詢地址名稱。語句:

create function fn_getaddress(v_id number) 
return varchar2
is
v_name varchar2(30); 
begin
select name into v_name from t_address where id=v_id; return v_name;
end;

測試此函數:

select fn_getaddress(3) from dual

輸出內容
在這裏插入圖片描述
需求:查詢業主ID,業主名稱,業主地址,業主地址使用剛纔創建的函數來實現。

select id 編號,name 業主名稱,fn_getaddress(addressid) 地址 from t_owners

查詢結果如下:
在這裏插入圖片描述

4.3 存儲過程

4.3.1 什麼是存儲過程

存儲過程是被命名的PL/SQL塊,存儲於數據庫中,是數據庫對象的一種。應用程序可以調用存儲過程,執行相應的邏輯。
存儲過程與存儲函數都可以封裝一定的業務邏輯並返回結果,存在區別如下:

  • 存儲函數中有返回值,且必須返回;而存儲過程沒有返回值,可以通過傳出參數返回多個值。
  • 存儲函數可以在select語句中直接使用,而存儲過程不能。過程多數是被應用程序所調用。
  • 存儲函數一般都是封裝一個查詢結果,而存儲過程一般都封裝一段事務代碼。

4.3.2 存儲過程語法結構

創建或修改存儲過程的語法如下:

CREATE [OR REPLACE] PROCEDURE 存儲過程名稱
(參數名 類型, 參數名 類型, 參數名 類型)
IS|AS
    變量聲明部分;
BEGIN
    邏輯部分
[EXCEPTION
    異常處理部分]
END;

參數只指定類型,不指定長度
過程參數的三種模式:

  • IN傳入參數(默認)
  • OUT傳出參數,主要用於返回程序運行結果
  • IN OUT傳入傳出參數

4.3.3 示例

4.3.3.1 創建不帶傳出參數的存儲過程:添加業主信息

--增加業主信息序列
create sequence seq_owners start with 11;
--增加業主信息存儲過程
create or replace procedure pro_owners_add (
v_name varchar2,
v_addressid number,
v_housenumber varchar2,
v_watermeter varchar2,
v_type number
)
is
begin
    insert into T_OWNERS
        values(seq_owners.nextval,v_name,v_addressid,v_housenumb er,v_watermeter,sysdate,v_type);
commit;
end

PL/SQL中調用存儲過程:

call pro_owners_add('趙偉',1,'999-3','132-7',1);

JDBC調用存儲過程:

/** 增加 */
public static void add(Owners owners){
    java.sql.Connection conn=null;
    java.sql.CallableStatement  stmt=null;
    try {
        conn=BaseDao.getConnection();
        stmt=conn.prepareCall("{call pro_owners_add(?,?,?,?,?)}");
        stmt.setString(1, owners.getName());
        stmt.setLong(2, owners.getAddressid());
        stmt.setString(3, owners.getHousenumber());
        stmt.setString(4, owners.getWatermeter());
        stmt.setLong(5, owners.getOwnertypeid());
        stmt.execute();
    } catch (SQLException e)  {
        e.printStackTrace();
    }finally {
        BaseDao.closeAll(null, stmt, conn);
    }
}

4.3.3.2 創建帶傳出參數的存儲過程

需求:添加業主信息,傳出參數爲新增業主的ID

--增加業主信息存儲過程
create or replace procedure pro_owners_add (
v_name varchar2, v_addressid number, v_housenumber varchar2, v_watermeter varchar2, v_type number,
v_id out number
)
is
begin
select seq_owners.nextval into v_id from dual;
insert into T_OWNERS
    values( v_id,v_name,v_addressid,v_housenumber,v_watermete r,sysdate,v_type );
commit;
end;

PL/SQL調用該存儲過程

declare
v_id number;--定義傳出參數的變量
begin
pro_owners_add('王旺旺',1,'922-3','133-7',1,v_id);
DBMS_OUTPUT.put_line('增加成功,ID:'||v_id);
end;

執行成功後輸出結果:
在這裏插入圖片描述
JDBC調用存儲過程

/** 增加 */
public static long add(Owners owners){
    long id=0;
    java.sql.Connection conn=null;
    java.sql.CallableStatement stmt=null;
    try {
        conn=BaseDao.getConnection();
        stmt=conn.prepareCall("{call pro_owners_add(?,?,?,?,?,?)}");
        stmt.setString(1,owners.getName());
       stmt.setLong(2,owners.getAddressid());
       stmt.setString(3,owners.getHousenumber());
       stmt.setString(4, owners.getWatermeter());
       stmt.setLong(5, owners.getOwnertypeid());
       stmt.registerOutParameter(6,OracleTypes.NUMBER);//註冊傳出參數類型
       stmt.execute();
       id=stmt.getLong(6);//提取傳出參數
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        BaseDao.closeAll(null, stmt, conn);
    }
    return id;
}

4.4 觸發器

4.4.1 什麼是觸發器

數據庫觸發器是一個與表相關聯的、存儲的 PL/SQL 程序。每當一個特定的數據操作語句(Insert,update,delete)在指定的表上發出,Oracle 自動地執行觸發器中定義的語句序列。
觸發器可用於

  • 數據確認
  • 實施複雜的安全性檢查
  • 做審計,跟蹤表上所做的數據操作等
  • 數據的備份和同步

觸發器分類

  • 前置觸發器(BEFORE)
  • 後置觸發器(AFTER)

4.4.2 創建觸發器的語法

語法:

CREATE [or REPLACE] TRIGGER	觸發器名
BEFORE|AFTER
[DELETE][[or] INSERT][[or]UPDATE [OF 列名]]
ON 表名
[FOR EACH ROW ][WHEN(條件)]
declare
......
begin
PLSQL 塊
end;

FOR EACH ROW作用是標註此觸發器是行級觸發器/語句級觸發器
在觸發器中觸發語句與僞記錄變量的值

觸發語句 :old :new
Insert 所有字段都是空(null) 將要插入的數據
Update 更新以前該行的值 更新後的值
delete 刪除以前該行的值 所有字段都是空(null)

4.4.3 案例

4.4.3.1 前置觸發器

需求:當用戶輸入本月累計表數後,自動計算出本月使用數 。

create or replace trigger tri_account_updatenum1
    before
    update of num1
    on t_account
    for each row
declare
begin
:new.usenum:=:new.num1-:new.num0;
end;

4.4.3.2 後置觸發器

需求:當用戶修改了業主信息表的數據時記錄修改前與修改後的值

--創建業主名稱修改日誌表:用於記錄業主更改前後的名稱
create table t_owners_log (
updatetime date, ownerid number, oldname varchar2(30), newname varchar2(30)
);

--創建後置觸發器,自動記錄業主更改前後日誌
create trigger tri_owners_log 
after
update of name
on t_owners
for each row
declare
begin
insert into t_owners_log values(sysdate,:old.id,:old.name,:new.name);
end;

測試

--更新數據
update t_owners set name='楊小花' where id=3; 
commit;
--查詢日誌表
select * from t_owners_log;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章