Oracle數據庫

一、數據庫類型

a.層次型數據庫
b.網狀型數據庫
c.關係型數據庫

二、數據庫操作

如何操作數據庫:

1.運行cmd打開控制檯窗口–>輸入sqlplus–>如果是普通用戶,輸入“用戶名/密碼@服務名”,如果是sys用戶用dba權限進入的話,輸入“用戶名/密碼@服務名 as sysdba”

2.使用第三方工具來操作oracle數據庫: PLSQL developer Toad DBVisual
表空間:在oracle中可以建立多個表空間,一個表空間可以關聯多個數據文件,一個數據文件只能和一個表空間關聯,默認系統的表空間是SYSTEM
創建表空間:
create tablespace 表空間名
datafile ‘url’ //表空間文件保存路徑
size 表空間大小
autoextend on //表空間容量不足自動擴容
例如:

create tablespace buaa
datafile 'D:\app\lenovo\oradata\orcl\buaa.dbf'
size 100M
autoextend on

創建用戶:
create user 用戶名
identitied by 密碼
default tablespace 默認表空間名
temporary tablespace 臨時表空間名
例如:

create user Scott
identified by tiger
default tablespace buaa //默認表空間名和創建表空間名保持一致
temporary tablespace TEMP

賦予用戶權限:
grant 權限1,權限2,…… to 用戶名
例如

grant connect,dba,resource to scott //注意不是connection和source

基本數據類型:
常用的:char varchar2 number date timestamp
創建表:
Create table 表名(
字段1 數據類型[(長度)],
字段2 數據類型[(長度)],
。。。。。。。。。

查詢數據:
select column1,column2,…from 表名
例如:

select * from t_user    //最好是寫成字段名。如:
select uid,uname,...from t_user

修改表結構,基本SQL語句
rowid:表示一條記錄在內存中地址,能夠唯一的表示一條記錄。
rownum:行號,可以限制查詢上來的記錄數

注意:只能寫rownum<數,不識別大於號,要想實現用rownum實現>來限制查詢,可以利用子查詢,把rownum 當以要普通的查詢的列名在來限制查詢

修改表結構:
增加列:在原來表基礎上增加列
alter table 表名 add(column1 數據類型,column2 數據類型…….)
例如

alter table student add(chinese number, english number)

修改列://在原來的標的列的基礎上進行修改,一般用於改數據類型
alter table 表名 modify(列名1 數據類型,列名2 數據類型……)
例如:

alter table student modify(name varchar2(50))

刪除列:
alter table 表名 drop column 列名//注意不僅有drop還有column,是drop column 這個整體
例如:

alter table t_student drop column English

刪除表:
drop table 表名
主鍵(primary key):能唯一表示身份的,只標識一條記錄,主鍵不能重複,一張表中只能有一個主鍵
設置主鍵:
alter table 表名 add constraint 鍵名 primary key(列名)
例如:

alter table t_user add constraint pk_uid primary key(id)

外鍵(foreign key):用來做表之間的關聯,一個表中的外鍵一定是其他表中的主鍵
設置外鍵:
alter table 表名 add constraint 鍵名 foreign key(列名) references 表名(列名)
例如:

alter table t_student add constraint fk_classid foreign key(classid) references class(id)

刪除鍵:
alter table 表名 drop constraint 鍵名
例如:

alter table t_student drop constraint fk_classid

增加數據SQL語句:
Oracle中的字符串都是用單引號括起來的。
Sysdate表達當前日期
Insert into 表名(列1, 列2,…) values(值1,值2,…)
例:

insert into student(id, name, birth, math, classid) values('106', '張遼 ', sysdate, 89, 4)

如果要插入的是該表中的所有列的數據
insert into 表名 values(值1,值2.。。。。)
例:)

insert into student values('107', '張龍', sysdate, 78, 69, 78, 4

查詢語句:可以在select語句表名的後面使用where語句來限制查詢的條件
例:查詢班級號爲3的學生

select * from student where classid='3'

在oracle中用“!=”或“<>”來表示不等於
例:查詢班級號不爲3的學生

select * from student where classid<>'3'

還可以使用邏輯去處符來限制查詢條件(and or not)
例:查詢數學成績在60到80之間的學生

select * from student where math>=60 and math<=80

上述內容還可以使用between ….and….來實現

select * from student where math between 60 and 80

使用is null 和 is not null 來判斷值是否爲空
例:查詢沒有語文成績的學生

select * from student where chinese is null

例:查詢英語成績爲空,並且語文成績不爲空的學生

select * from student where english is null and chinese is not null

還可以使用in關鍵字來限制查詢範圍
例:

select * from student where math in (95, 65, 25)

通常用dual表來進行一些函數以及數據的測試

select sysdate from dual
select sys_guid() from dual

模糊查詢:
通配符:% _
%:0-n個字符
_:一個字符

例:查詢student表中姓張的同學

select * from student where name like '張%'

例:查詢student表中姓張的,並且姓名是兩個字的同學

select * from student where name like '張_'

刪除數據:
delete from 表名 [where 條件]
例:delete from student –不建議這樣寫,除非你想刪除全表數據
刪除id號爲101的記錄

delete from student where id='101'

更新語句:
update 表名 set 列名=值, 列名=值。。。。。。[where 條件]
建議寫where條件,否則做的是全表更新
例:姓張的同學的,並且姓名是兩個字的,將他們的數學成績提高到90分

update student set math=90 where name like '張_'

rowid:表示一條記錄在內存中的地址,能夠唯一標識一條記錄。
rownum:行號,可以限制查詢所得到的記錄數。
注意:在寫查詢語句時注意空格,在寫表名,字段名時,可以空一格寫別名
如:select t.name,t.age from teacher t

修改表結構:

  1. 增加列
    alter table 表名 add(列名1 數據類型,列名2 數據類型,…….)
    如:
 alter table teacher add(english number,math number)

2.修改列
alter table 表名 modify(列名1 數據類型,列名2 數據類型,…….)
如:

alter table teacher modify(name varchar2(30))

3.刪除列
alter table 表名 drop column 列名
如:

 alter table teacher teacher drop column english

4.刪除表
drop table 表名

增加刪除主鍵,附上表結構

create table t_member(
       tid varchar2(100),
       tname varchar2(10),
       tage number,
       tbirth date,
       cid number
)
create table t_class(
       cid number,
       cname varchar2(10),
       cage number,
       cbirth date
)
alter table t_member add constraint pk_tid primary key(tid)

alter table t_class add constraint pk_cid primary key(cid)
alter table t_member add constraint fk_classid foreign key(cid) references t_class(cid)

alter table t_member drop constraint pk_tid
alter table t_member drop constraint fk_classid

**主鍵(**primary key):能夠唯一表示一條記錄,主鍵不能重複,並且一張表中只能有一個主鍵,具有唯一性。
添加主鍵:alter table 表名 add constriant 鍵名 primary key(列名)
如:

alter table teacher add constriant pk_tid primary key(id)

外鍵:(foreign key):用來做表之間關聯作用的,一個表中的外鍵一定是另一個表的主鍵。
添加外鍵:Alter table 表名 add constraint 鍵名 foreign key(列名) refrences 表名(列名)
如;

alter table teacher add constriant fk_classid foreign key(classid) references class(id)

刪除鍵: alter table 表名 drop constriant 鍵名
如:

alter table teacher drop constriant fk_classid

增加數據SQL:
oracle中的字符串都是使用單引號括起來的。
sysdate表示當前系統時間。
插入數據:
insert into 表名(column1,column2,….) values(值1,值2,……)
如:

insert into teacher(id,name,age,birth,classid) values ('10','tom',25,sysdate,2)

插入所有列數據可以直接寫成:
insert into 表名 values(值1,值2.。。。。)
例:

insert into teacher values('107', '張龍', sysdate, 78, 69, 78, 4)

限制查詢語句:使用where以達到限制查詢條件
例如:查詢班級號爲3的學生

select * from student where classid='3'

在oracle中用“!=”“<>”來表示不等於
例:查詢班級號不爲3的學生

select * from student where classid<>'3'

還可以使用邏輯去處符來限制查詢條件(and or not
例:查詢數學成績在60到80之間的學生

select * from student where math>=60 and math<=80

上述內容還可以使用between ….and….來實現

select * from student where math between 60 and 80

使用is nullis not null 來判斷值是否爲空
例:查詢沒有語文成績的學生

select * from student where chinese is null

例:查詢英語成績爲空,並且語文成績不爲空的學生

select * from student where english is null and chinese is not null

還可以使用in關鍵字來限制查詢範圍
例:
select * from student where math in (95, 65, 25)

通常用dual表來進行一些函數以及數據的測試

select sysdate from dual
select sys_guid() from dual

模糊查詢:
通配符:% %:0-n個字符 :一個字符

例:查詢student表中姓張的同學

select * from student where name like '張%'

例:查詢student表中姓張的,並且姓名是兩個字的同學

select * from student where name like '張_'

刪除數據:
delete from 表名 [where 條件]
例:delete from student –不建議這樣寫,除非你想刪除全表數據
刪除id號爲101的記錄

delete from student where id='101'

更新語句:
update 表名 set 列名=值, 列名=值。。。。。。[where 條件]
建議寫where條件,否則做的是全表更新
例:姓張的同學的,並且姓名是兩個字的,將他們的數學成績提高到90分

update student set math=90 where name like '張_'

三、單行函數

單行函數的功能非常的簡單就是完成某一個功能,例如:現在希望將字母變爲大寫,這樣的功能就可以通過單行函數完成,在 Oracle 之中提供的單行函數非常的多,今天主要是看核心的幾個。
單行函數分類爲:字符串函數數字函數日期函數轉換函數通用函數。所有的單行函數在進行記憶的時候都要求清楚的記下單行函數的名稱、返回值類型、接收的參數數據。

1、字符串函數

字符串函數的主要功能是進行字符串處理,在oracle內部提供了一個dual的虛擬表,可以用於測試。
1.大小寫轉換函數

  • 轉大寫:字符串 UPPER(字符串 | 數據列)
  • 轉小寫:字符串 LOWER(字符串 | 數據列)
    例如:
select upper('hello'),lower('HELLO') from dual;
SELECT LOWER(ename) FROM emp ;

2.首字母大寫,其餘字母小寫:字符串 initcap(字符串 | 列)
例如:

select initcap(ename) from emp;

3.取得字符串長度,數字 length (字符串 | 列)
例如:

select length('hello world') from daul
SELECT ename,LENGTH(ename) FROM emp ;
--查詢出所有僱員姓名的長度爲 5 的全部僱員信息
SELECT ename,LENGTH(ename)FROM emp WHERE LENGTH(ename)=5 ;

4.替換指定字符串的內容:字符串 REPLACE(字符串 | 列,要替換的內容,新的內容)

--替換字符串數據
SELECT REPLACE('hello world','l','_') FROM dual ;
--替換數據列
SELECT ename,REPLACE(ename,'A','_') FROM emp ;

5.字符串自取操作
· 由指定位置截取到結尾:字符串 substr(字符串 | 數據列,截取開始索
引);
· 指定截取的開始和結束位置:字符串 substr(字符串 | 數據列,截取開
始索引,截取結束索引);

--字符串截取,由指定位置截取到結尾
SELECT SUBSTR('hello world',7) FROM dual ;
--字符串截取,部分截取
SELECT SUBSTR('hello world',0,5) FROM dual ;
SELECT SUBSTR('hello world',1,5) FROM dual ;

注意:實際上在 Oracle 之中,字符串的索引下標是從 1 開始的(程序是從 0 開始的),但是爲了考慮到程序人員的使用習慣,所以即使設置了 0,那麼也表示從1 開始,這一點會由 Oracle 自己進行處理。

--截取僱員姓名前三位
SELECT ename,SUBSTR(ename,1,3) FROM emp ;
--截取僱員姓名後三位
SELECT ename,SUBSTR(ename,-3) FROM emp ;

Tip:請問 Oracle 之中的 SUBSTR()函數進行截取的時候下標是從 0 還是從 1開始?
· 實際上從 0 還是從 1 開始沒有任何的區別, Oracle 之中的字符串是從 1 開始的,但是即使設置了 0,也表示從 1 開始,同時 SUBSTR()函數還可以設置爲負數,表示由後的指定位置開始。

6.去掉左右空格函數(功能類似Java中提供的trim方法):字符串 trim (字符串 | 列)

SELECT ' hello world ',TRIM(' hello world ') FROM dual ;

2、數字函數

1.四捨五入函數:數字 round (數字 | 列[,保留小數位])

--使用dual表驗證round()函數
SELECT ROUND(9615.7652) ,ROUND(9615.7652,2) ,ROUND(9615.7652,-2) ,ROUND(-15.5) FROM dual

2.截取小數函數:數字 trunc (數字 | 列 [,保留小數位])

--使用dual表驗證trunc()函數
SELECT TRUNC(9615.7652) ,TRUNC(9615.7652,2) ,TRUNC(9615.7652,-2) ,TRUNC(-15.5) FROM dual

3.求模:數字 mod(數字1 | 列1,數字2 | 列2)

SELECT MOD(10,3) FROM dual 

3、日期函數

如果要想操作日期函數實際上有一個首要的問題需要解決。那麼必須要首
先取得當前的系統日期時間,爲此在 Oracle 裏面提供有兩個僞列(是不存在表中的列,但是卻可以直接使用的列):SYSDATESYSTIMESTAMP

SELECT SYSDATE FROM dual ;
SELECT SYSTIMESTAMP FROM dual ;

簡單的日期處理:
· 日期 + 數字 = 日期,表示若干天之後的日期;
· 日期 – 數字 = 日期,表示若干天之前的日期;
· 日期 – 日期 = 數字,表示兩個日期之間的間隔天數。

--計算若干天之後的日期
SELECT SYSDATE + 3 , SYSDATE + 120 FROM dual ;
--計算若干天之前的日期
SELECT SYSDATE - 120 FROM dual ;
--統計僱員的受僱天數
ELECT ename, SYSDATE - hiredate FROM emp ;

這種計算時間的方式不是很準確,若果要得到準確的時間,需要利用提供的日期函數
1.計算兩個日期之間經歷的月數:數字 months_between(日期1 | ;列1,日期2 | 列2)

--計算每一個僱員到現在爲止受僱的年數(年數計算最準確的是通過月份計算的)
SELECT ename,MONTHS_BETWEEN(SYSDATE,hiredate) ,MONTHS_BETWEEN(SYSDATE,hiredate)/12 FROM emp ;

SELECT ename,MONTHS_BETWEEN(SYSDATE,hiredate) ,TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) FROM emp ;
--查詢出所有已經工作了十年以上的員工
SELECT * FROM emp WHERE MONTHS_BETWEEN(SYSDATE,hiredate)/12>10 ;

2.加上指定月之後的日期:日期 add_months(日期 | 列,月數)

--計算若干月之後的日期
SELECT SYSDATE+30,ADD_MONTHS(SYSDATE,12),ADD_MONTHS(SYSDATE,100) FROM dual ;

Tips:在進行計算的時候,數字的大小是不受限制的。
複雜日期求解題:要求計算出每一位僱員到今天爲止僱傭的年限,Tom 的僱傭日期是:1991 年 01月 01 日

--第一步:計算出每一位僱員到今天爲止的僱傭年份,直接利用月數除 12 即可
SELECT ename,hiredate, TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year FROM emp ;

--第二步:計算月,在進行年計算的時候發現存在有小數,這些小數就包含了月
SELECT ename,hiredate, TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year ,TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months FROM emp ;

--第三步:計算天數,對於天數的計算實際上只學會了一個公式“日期 1 – 日期 2 =天數”,於是--分析日期:
--· 日期 1:應該是當前日期“SYSDATE”;
--· 日期 2:應該是已經剔除掉了經過月之後的日期。
SELECT ename,hiredate,TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year ,TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months ,TRUNC(SYSDATE -ADD_MONTHS(hiredate,MONTHS_BETWEEN(SYSDATE,hiredate))) day FROM emp ;

3.求解指定下週星期的日期:日期 next_day(日期 | 列,一週時間數)

--計算下一個星期五
SELECT NEXT_DAY(SYSDATE,'星期五') FROM dual ;

4.求出指定日期所在月的最後一天日期:日期 last_day(日期 | 列)

--求出本月最後一天日期
SELECT LAST_DAY(SYSDATE) FROM dual ;
--查詢出在每個僱員受僱月的倒數第二天僱傭的僱員信息
SELECT ename,hiredate,LAST_DAY(hiredate)-2 FROM emp WHERE LAST_DAY(hiredate)-2=hiredate ;

4、轉換函數

1、 轉字符串數據:字符串 TO_CHAR(數字 | 日期 | 列,轉換格式)
在進行轉換格式設置的時候要根據不同的數據類型進行格式標記的定義:
· 日期:年(yyyy)、月(mm)、日(dd)、時(HH、HH24)、分
(mi)、秒(ss);
· 數字:一位任意數字(9)、本地貨幣(L)。

--將日期格式化爲字符串
SELECT TO_CHAR(SYSDATE,'yyyy-mm-dd') FROM dual ; 
SELECT TO_CHAR(SYSDATE,'fmyyyy-mm-dd') FROM dual ; 
--在 Oracle 的 SYSDATE 裏面是包含有時間數據的,也可以繼續顯示時間
SELECT TO_CHAR(SYSDATE,'yyyy-mm-dd hh24:mi:ss') FROM dual ;

那麼實際上用戶也可以利用 TO_CHAR()函數進行年、月、日數據的拆分。

--拆分日期數據
SELECT TO_CHAR(SYSDATE,'yyyy') year ,TO_CHAR(SYSDATE,'mm') months ,TO_CHAR(SYSDATE,'dd') day FROM dual ;
--查詢出所有在 2 月份僱傭的僱員
SELECT * FROM emp WHERE TO_CHAR(hiredate,'mm')='02' ;
--使用 TO_CHAR()還可以格式化數字顯示。
--格式化數字顯示
SELECT TO_CHAR(234789234798,'L999,999,999,999,999') FROM dual ;

2、 轉日期數據:日期 TO_DATE(字符串,轉換格式)

--將字符串變爲日期
SELECT '1981-09-15',TO_DATE('1981-09-15','yyyy-mm-dd') FROM dual ;

但是在 Oracle 之中提供有自動的轉換方式,如果字符串按照日期的格式編寫,那麼可以自動由字符串變爲日期。

3、 轉數字類型:數字 TO_NUMBER(字符串,轉換格式)

--將字符串變爲數字
SELECT TO_NUMBER('1') + TO_NUMBER('2') FROM dual ;
SELECT '1' + '2' FROM dual ;

在 Oracle 裏面這些自動的數據類型轉換功能是非常方便的,包括日期和字符串、字符串和數字。

5、通用函數

通用函數主要是 Oracle 的特色函數,有兩個:NVL()、DECODE()。
1 處理 null 數據:nvl(字符串1, 字符串2) 當字符串1爲空的時候,用字符串2代替

--要求計算每一位僱員的年薪
SELECT ename,sal,comm,(sal+comm)*12 FROM emp ;
select name, nvl(math, 0) + 1 from student;

這個時候發現有一部分人是沒有年薪的,而這部分人都是沒有佣金的,因
爲在數據庫之中,null 與任何的數據進行數學計算其結果都是 null,那麼如果遇到了 null 應該按照數字 0 來處理,所以就可以使用 NVL()函數。

SELECT ename,sal,comm,(sal+NVL(comm,0))*12 FROM emp ;

nvl2(字符串1,字符串2,字符串3) 如果字符串1不爲空,返回字符串2,否則返回字符串3

select name, nvl2(math, 1, 0) from student

nullif(字符串1, 字符串2) 如果兩個字符串相同返回null,否則返回字符串1

select nullif('a', 'a') from dual

2 多數據判斷:數據 DECODE(字段 | 數據,判斷數據 1,顯示數據 1,判斷數據 2,顯示數據 2,… [默認顯示])

--將所有僱員的職位數據變爲中文
SELECT ename,job ,DECODE(job,'CLERK','辦事員','SALESMAN','銷售','MANAGER','經理','ANALYST','分析員','PRESIDENT','總裁','未處理') FROM emp ;

四、多表查詢

1、多表查詢基本操作

多表查詢的語法如下:
SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,… FROM 數據表 [別名] , 數據 [別名] ,… [WHERE 條件(s)] [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,…]

使用count()函數統計表中的數據記錄

--統計emp表中的數據量
select count(*) from emp;
--統計dept表中的數據量
select count(*) from dept;
--emp與dept實現多表查詢
select * from emp,dept;

以上實現的多表查詢會出現“笛卡爾積”的查詢結果,比如emp有8條記錄,dept有10條記錄,會產生8*10種記錄,出現大量重複數據。如何消除“笛卡爾積”?唯一的方案就是使用關聯字段。

--解決方案
select * from emp,dept where emp.deptno=dept.deptno;

對於複雜表名的查詢可以設置別名進行查詢。

--使用別名
select * from emp e,dept d where e.deptno=d.deptno;

範例:要求查詢出每個僱員的編號、姓名、職位、工資、部門名稱、部門位置
· 確定要使用的數據表:
|- emp 表:僱員的編號、姓名、職位、工資;
|- dept 表:部門名稱、部門位置;
· 確定已知的關聯條件:
|- 僱員和部門:emp.deptno = dept.deptno。

--查詢每個僱員的編號,姓名,職位,工資
select e.empno,e.ename,e.job,e.sal from emp e;
--加入部門表之後進行查詢
select e.empno,e.ename,e.job,e.sal,d.dname,d.loc from emp e,dept d where e.deptno=d.deptno;

範例:查詢出每個僱員的編號、姓名、職位、工資、僱傭日期、工資等級
· 確定要使用的數據表:
|- emp 表:僱員的編號、姓名、職位、工資、僱傭日期;
|- salgrade 表:工資等級;
· 確定已知的關聯條件:
|- 僱員與工資等級:emp.sal BETWEEN salgrade.losal AND
salgrade.hisal;

--查詢僱員數據
select e.empno,e.ename,e.job,e.sal,e.hiredate from emp e;
--加入工資等級表,使用範圍查詢
select e.empno,e.ename,e.job,e.sal,e.hiredate,s.grade from emp e,salgrade s
where e.sal between s.losal and s.hisal ;

範例:查詢每個僱員的編號、姓名、職位、僱傭日期、工資、工資等級、所在部門名稱及位置
· 確定要使用的數據表:
|- emp 表:僱員的編號、姓名、職位、僱傭日期、工資;
|- salgrade 表:工資等級;
|- dept 表:部門名稱及位置。
· 確定已知的關聯條件:只要是消除笛卡爾積的條件之間都使用 AND 連接
|- 僱員和工資等級:emp.sal BETWEEN salgrade.losal AND
salgrade.hisal;
|- 僱員和部門:emp.deptno = dept.deptno。

--查詢每個僱員的所有信息
selecte.empno,e.ename,e.job,e.sal,e.hiredate from emp;
--增加工資等級判斷
select e.empno,e.ename,e.job,e.sal,e.hiredate,s.grade from emp e,salgrade s where e.sal between s.losal and s.hisal ;
--增加部門表信息
select e.empno,e.ename,e.job,e.sal,e.hiredate,s.grade,d.name,d.loc from emp e,salgrade,s,dept,d where e.sal between s.losal and s.hisal and e.deptno=d.deptno;

Tips: 關於數據量龐大的兩個說明
· 說明一:在使用 NOT IN 查詢的時候裏面不能有 NULL
如果說現在拿到一張數據表(選擇的 costs 表,這張表第一次接觸)。如果是一位菜鳥使用此表,往往會直接發出如下的一行指令:
SELECT * FROM costs ;
於是此時就悲哀了,如果此表之中的數據量較大,那麼最輕的後果是你查
看不方便,而嚴重的後果是死機(中國銀行的數據庫死機一秒種會有一羣人被幹掉的)。但 是如果是有經驗的開發者,往往都先使用 COUNT()函數確定一下表中的數據量。如果數據量較大,那麼使用特定的操作取出特定的幾行數據,如果數據量小, 則隨便操作。
SELECT COUNT(*) FROM costs ;
那麼回到 NOT IN 之中不能出現 null 的問題,如果 NOT IN 裏面有了 null 則表示不爲空,那麼有些字段的數據是永恆不能爲空的,那麼就表示查詢全部了。如果表中的數據量龐大了,那麼直接造成死機。
· 說明二:以上的查詢只是消除了顯示中的笛卡爾積,但是笛卡爾積仍然存
在,下面來驗證一下笛卡爾積影響。
SELECT COUNT(*) FROM costs ;
SELECT COUNT(*) FROM sales ;
SELECT COUNT(*)
FROM costs c,sales s
WHERE c.prod_id=s.prod_id ;
雖然最終的操作已經成功的消除掉了顯示的笛卡爾積,但是遺憾的是整個
運算執行過程非常的緩慢,所以在實際的工作之中,尤其是在你進行數據表設計的時候應該儘可能的避免查詢有可能出現的笛卡爾積問題。
結論:在數據量大的時候絕對不要採用多表查詢,而且就算是數據量小,
也別用多表查詢。

2、連接方式

對於多表查詢來講其有兩種表的連接方式:
· 內連接:也被稱爲等值連接,在之前的所有查詢都屬於內連接;
· 外連接:左外連接、右外連接、全外連接。

--增加一個沒有部門的數據
insert into emp(empno,ename,job,hiredate,sal) values(1001,'公子扶蘇','clerk',sysdate,108);
此時新增員工所在部門爲null

範例:

select*from emp e,dept,d where e.deptno=d.deptno;

如果要想讓沒有部門的僱員或者是沒有僱員的部門數據顯示,則可以採用
如下的方式實現外連接:
· 左外連接:字段 = 字段(+),“(+)”在等號右邊表示左外連接;
· 右外連接:字段(+) = 字段,“(+)”在等號左邊表示右外連接;

--左外連接
select*from emp e,dept,d where e.deptno=d.deptno(+);
--右外連接
select*from emp e,dept,d where e.deptno(+)=d.deptno;

一般而言,如果現在需要查詢的數據沒有出現,纔會使用到外連接,沒有
必要去強制性的去分清楚到底是左還是右。

範例:查詢每個僱員的編號、姓名、職位、領導姓名
實際上在 emp 表之中存在有一個 mgr 的字段,這個字段保存的是每一位僱員對應的領導編號。
· 確定所需要的數據表:
|- emp 表:僱員的編號、姓名、職位;
|- emp 表:領導姓名;
· 確定已知的關聯字段:
|- 僱員和領導:emp.mgr = memp.empno(僱員的領導編號 = 僱員編
號)。

--查詢emp表
select e.empno,e.ename,e.job from emp e ;
--查詢出領導的姓名使用內連接,等值連接,有 null 的不顯示
select e.empno,e.ename,e.job,m.ename from emp e ,emp m where e.mgr=m.empno;
--加入外連接控制,讓所有的僱員數據顯示
select e.empno,e.ename,e.job,m.ename from emp e ,emp m where e.mgr=m.empno(+);

外鏈接在實際操作中非常重要,以上查詢是自關聯查詢。。。。

3、SQL:1999 語法支持

SQL 語法標準實際上一直在進行更新,從 1999 年之後對於數據表的關聯查詢給了一個標準的操作語法(因爲“(+)”符號只有 Oracle 可以使,那麼其它的數據庫不支持此類符號,只能夠使用 SQL:1999 語法完成),此類語法定義如下:

SELECT * | 字段 [別名]
FROM 表 [CROSS JOIN 表] [NATURAL JOIN 表]
[JOIN] [USING(字段)] [ON(條件)]
[LEFT | RIGTH | FULL | OUTER JOIN 表] ;

1、 交叉連接,其主要的目的是爲了產生笛卡爾積
語法:
SELECT * | 字段 [別名] FROM 表 CROSS JOIN 表 ;
範例:

SELECT * FROM emp CROSS JOIN dept ;

2、 自然連接,主要是消除掉笛卡爾積(內連接)
語法:
SELECT * | 字段 [別名] FROM 表 NATURAL JOIN 表 ;
範例:

SELECT * FROM emp NATURAL JOIN dept ;

3、 使用 USING 子句,用戶指定關聯字段
語法:
SELECT * | 字段 [別名] FROM 表 JOIN 表 USING(字段);
範例:

SELECT * FROM emp JOIN dept USING(deptno) ;

4、 使用 ON 子句指定關聯條件
語法:
SELECT * | 字段 [別名] FROM 表 JOIN 表 ON(條件);
範例:

SELECT * FROM emp e JOIN dept d ON(e.deptno=d.deptno) ;

5、 外連接
語法:
SELECT * | 字段 [別名] FROM 表 LEFT | RIGTH | FULL OUTER JOIN 表];
範例:

SELECT * FROM emp e LEFT OUTER JOIN dept d ON (e.deptno=d.deptno);
SELECT * FROM emp e RIGHT OUTER JOIN dept d ON (e.deptno=d.deptno);
SELECT * FROM emp e FULL OUTER JOIN dept d ON (e.deptno=d.deptno);

4、查詢結果連接

在數學之中存在交集、並集、差集的概念,那麼此概念在 SQL 語句之中也同樣存在。在 SQL 語法之中提供了:UNION、UNION ALL、INTERSECT、MINUS 幾個符號實現結合操作,這幾個符合可以連接多個 SQL,使用的基本語法如下:

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,…
FROM 數據表 [別名] , 數據表 [別名] ,…
[WHERE 條件(s)]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,…]
[UNION | UNION ALL | INTERSECT | MINUS]
SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,…
FROM 數據表 [別名] , 數據表 [別名] ,…
[WHERE 條件(s)]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,…]
[UNION | UNION ALL | INTERSECT | MINUS]
SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,…
FROM 數據表 [別名] , 數據表 [別名] ,…
[WHERE 條件(s)]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,…]
….

範例:驗證 UNION 操作,不顯示重複記錄

SELECT * FROM emp WHERE deptno=10
UNION
SELECT * FROM emp ;

範例:驗證 UNION ALL 操作,顯示所有數據,包含重複數據

SELECT * FROM emp WHERE deptno=10
UNION ALL
SELECT * FROM emp ;

範例:驗證 INTERSECT 操作,返回相同的部分,交集

SELECT * FROM emp WHERE deptno=10
INTERSECT
SELECT * FROM emp ;

範例:驗證 MINUS 操作,返回不同的部分,是一個差集


SELECT * FROM emp
MINUS
SELECT * FROM emp WHERE deptno=10 ;

在工作之中有部分的查詢會利用以上的方式以簡化查詢的複雜度。

五、分組統計查詢

1、組函數

常用的統計函數有:
個數count()、求和sum()、平均avg()、最大值max()、最少值min()

--統計所有僱員的人數,支付的總工資,平均工資,最高工資,最低工資
select count(*),sum(sal),avg(sal),max(sal),min(sal) from emp;
--統計公司支付的總年薪和平均薪資
select sum((sal+nvl(comm,0))*12),avg((sal+nvl(comm,0))*12) from emp;
--求出公司最早僱傭僱員的日期和最晚僱傭僱員的日期
select max(hiredate),min(hiredate) from emp;

面試題:
請解釋“count(*)”、“count(字段)”、“count(distinct 字段)”的區別?

select count(*),count(ename),count(comm),count(distinct job) from emp;

輸出結果

COUNT(*) COUNT(ENAME) COUNT(COMM) COUNT(DISTINCTJOB)
---------- ------------ ----------- ------------------
        19           19          13                  8

· COUNT(*):會實際的統計出表中的數據量;

· COUNT(字段):
|- 如果統計的字段上不包含有 null,那麼與 COUNT(*)結果相同;
|- 如果統計字段上包含有了 null,null 不參與統計;

· COUNT(DISTINCT 字段):消除掉重複數據後的統計。

2、分組統計

實際上所謂的分組,指的某些羣體具備共同的特徵。現在回到 emp 表中,可以發現 job 和 deptno 兩個字段之中都存在有重複的數據,那麼就證明這兩個字段上都可以進行分組。

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,… | 統計函數
FROM 數據表 [別名] , 數據表 [別名] ,…
[WHERE 條件(s)]
[GROUP BY 分組字段, 分組字段, …]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC |
DESC] ,…]

--按職位分組,統計每個職位的平均工資、最高和最低工資、人數
select job,avg(sal),max(sal),min(sal),count(*) from emp group by job;
--按照部門編號跟組,統計每個部門的個數、平均工資、平均服務年限
--select deptno,count(empno),avg(sal),avg(months_between(sysdate,hiredate)/12) from emp group by deptno;

提示:關於統計查詢的幾個重要說明。
以上的代碼只是根據基礎語法實現了統計的操作,但是在整個操作之中還
會存在有三個限制:
· 限制一:統計函數單獨使用時(沒有 GROUP BY 子句)只能夠出現
統計函數,不能夠出現其它字段。
正確的代碼:

SELECT COUNT(empno) FROM emp ; 

錯誤的代碼:

SELECT COUNT(empno),ename FROM emp ;

· 限制二:使用統計查詢時(存在 GROUP BY 子句),SELECT 子句之中只允許出
現統計函數與分組字段,其它的任何字段都不允許出現。
正確的代碼:

SELECT deptno,COUNT(empno)
FROM emp
GROUP BY deptno ;

錯誤的代碼:

SELECT deptno,COUNT(empno),ename
FROM emp
GROUP BY deptno ;

· 限制三:統計函數在分組之中可以嵌套使用,但是嵌套之後的統計查詢之中,
SELECT 子句不允許再出現任何的字段,包括分組字段。
正確的代碼:

SELECT MAX(COUNT(empno))
FROM emp
GROUP BY deptno ;

錯誤的代碼:

SELECT deptno,MAX(COUNT(empno))
FROM emp
GROUP BY deptno ;

在整個分組操作之中,以上的三個限制可以說是最難理解的。一定要搞清
楚。
以上的操作都是針對於單張表一個字段的分組,那麼通過之前給出的語法
格式可以發現,實際上分組也可以同時設置多個字段,那麼這就要求這多個字段必須同時重複。
1.查詢出每個部門名稱、部門人數、平均工資
第一步:先不考慮分組問題,換個角度,實現查詢,查詢出每個部門名稱、僱員編號、工資

SELECT d.dname,e.empno,e.sal FROM emp e,dept d WHERE e.deptno=d.deptno ;

第二步:在以上的查詢結果之中發現了部門名稱的數據是重複的,但是這個時候並不是直接針對於一張實體表進行的分組操作, 而是針對於一張臨時查詢結果進行分組操作,由於 GROUP BY 在 WHERE 之後執行,所以直接分組。

SELECT d.dname,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno=d.deptno GROUP BY d.dname ;

第三步:現在既然已經引入了 dept 表,那麼就表示存在有 4 個部門數據,而此時只有三個部門信息,所以需要繼續使用外連接進行 40 部門數據的顯示。

SELECT d.dname,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.dname ;

2.查詢出每個部門的編號、名稱、位置、部門人數、平均工資
· 確定所需要的數據表
|- dept 表:找到部門編號、名稱、位置;
|- emp 表:統計部門人數、平均工資。
第一步:首先先不去考慮分組,先實現多表查詢,查詢部門編號、名稱、位置、僱員編號、工資

SELECT d.deptno,d.dname,d.loc,e.empno,e.ename FROM emp e,dept d WHERE e.deptno=d.deptno ;

第二步:現在發現部門的數據三個字段都是在整體重複着,那麼直接針對於三個字段實現分組

SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.deptno,d.dname,d.loc ;

3.按照職位分組,統計每個職位的平均工資,要求顯示的是平均工資高於
2000 的職位信息

SELECT job,AVG(sal) FROM emp WHERE AVG(sal)>2000 GROUP BY job ;
運行結果:
WHERE AVG(sal)>2000
*
第 3 行出現錯誤:
ORA-00934: 此處不允許使用分組函數

但是在執行之後發現了錯誤的提示信息,意思是 WHERE 子句裏面不能夠使用統計函數,而且通過之前的分析也應該知道,WHERE 是在 GROUP BY 之前使用的, 而此時的條件明顯是在 GROUP BY 之後的過濾, 所以此時只能夠使用 HAVING子句完成,語法如下:

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,… | 統計函數
FROM 數據表 [別名] , 數據表 [別名] ,…
[WHERE 條件(s)]
[GROUP BY 分組字段, 分組字段, …]
[HAVING 分組後過濾]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC |
DESC] ,…]

--使用having修改程序
select job,avg(sal) from emp group by job having avg(sal)>2000;

關於 WHERE 和 HAVING 的區別?

· WHERE 子句:是在分組前使用,而且不能夠使用統計函數進行驗證,經過 WHERE 篩選後的數據纔可以分組;

· HAVING 子句:必須結合 GROUP BY 子句一起出現,是在分組後的過濾,可以使用統計函數。

思考題一:統計公司每個工資等級的人數、平均工資
· 確定要使用的數據表:
|- salgrade 表:工資等級;
|- emp 表:統計人數、平均工資;
· 確定已知的關聯字段:
|- 工資與工資等級:emp.sal BETWEEN salgrade.losal AND
salgrade.hisal。
第一步:查詢出工資等級,以及每個僱員的編號、工資,直接實現多表查詢

SELECT s.grade,e.empno,e.sal FROM emp e,salgrade s WHERE e.sal BETWEEN s.losal AND s.hisal ;

第二步:針對於臨時數據表之中的數據進行分組

SELECT s.grade,COUNT(e.empno),AVG(e.sal) FROM emp e,salgrade s WHERE e.sal BETWEEN s.losal AND s.hisal GROUP BY s.grade ;

思考題:統計出公司領取佣金和不領取佣金的人數、平均工資
現在很容易聯想到,直接針對於 comm 字段分組,於是寫出瞭如下的語句。

SELECT comm,COUNT(empno),AVG(sal) FROM emp GROUP BY comm ;

所以此時這個查詢結果發現並不理想,那麼不如換個思路。
第一步:統計出所有領取佣金的人數、平均工資

SELECT '領取佣金',COUNT(empno),AVG(sal) FROM emp WHERE comm IS NOT NULL ;

第二步:統計出所有不領取佣金的人數、平均工資

SELECT '不領取佣金',COUNT(empno),AVG(sal) FROM emp WHERE comm IS NULL ;

第三步:以上兩個查詢返回的結構完全相同,那麼就將其變爲一個結果,使用UNION

SELECT '領取佣金',COUNT(empno),AVG(sal) FROM emp WHERE comm IS NOT NULL
UNION
SELECT '不領取佣金',COUNT(empno),AVG(sal) FROM emp WHERE comm IS NULL ;

很多時候都可以直接進行分組,但是在進行分組的時候,必須保證數據列
上有重複,但是如果是針對於一些條件,那麼只能夠利用以上的方式完成。

發佈了46 篇原創文章 · 獲贊 48 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章