/*******************************************************************************************/
一、SQL簡介
SQL:結構化查詢語言
SQL 是一門 ANSI 的標准計算機語言,用來訪問和操作數據庫系統。
SQL 語句用於取回和更新數據庫中的數據。SQL 可與數據庫程序協同工作,比如 MS Access、DB2、Informix、MS SQL Server、Oracle、Sybase 以及其他數據庫系統。
注釋:除了 SQL 標准之外,大部分 SQL 數據庫程序都擁有它們自己的私有擴展!
SQL語法基礎:
select... 查詢內容
from... 在哪里查詢
where... 使用什么樣的條件
group by...(sql的入門) 按什么進行分組
having... 做分組時按什么條件過濾
order by... 按什么來排序
/*******************************************************************************************/
二、基本的操作
0.顯示當前用戶: SQL> show user;
1.查看當前用戶下,有幾個表的指令:select * from tab;//tab是數據字典,記錄的是scott用戶下有哪些表
2.查看表結構:
desc dept(dept部門表)
DEPTNO 部門編號(這些查看信息都可以用select * from dept來看)
DNAME 部門名稱
LOC 地點
desc emp(emp員工表)
EMPNO 員工號
ENAME 員工姓名
JOB 工種
MGR 經理
HTREDATE入職日期
SAL 薪水
COMM 獎金
DEPTNO 部門編號(兩個表都有的字段,這個是關系型數據庫的特點,兩個表可以有同一個字段)
3.查詢命令可以使用select * from dept
4.其他
1).
select * from emp//結果出來很多東西,格式很亂,解決方法:
set linesize 140;//包括空格一行的大小為140,也就是設置一行的寬度
/ 扛表示執行上一條命令,也就是查詢命令
set pagesize 120;//設置一頁的寬度
/
也可以把命令寫入到如下兩個配置文件里,這樣登錄退出后也會生效,
C:\app\Administrator\product\11.2.0\client_1\sqlplus\admin\glogin.sql
C:\app\Administrator\product\11.2.0\dbhome_1\sqlplus\admin\glogin.sql
2).
select * from dept;
col deptno for(format) 9999;//設置deptno列寬度為4個9這么寬(這個是數值型的設置)
col dname for a20;//設置dname 列寬度為20(這個是字符型的設置)
3).
清屏命令:host cls
/*******************************************************************************************/
三、基本的查詢
1.查詢的語法
select *|{[distinct]column|expression[alias],..} from table;
基本查詢:可以查詢全部列,部分列,表達式,別名,去除重復
2.示例:
1).基本查詢
--查詢員工表信息
SQL> select * from emp;
2).as后面是別名,別名的意思是查詢后的結果不顯示原來的名字顯示定義的別名。其中as可以省略。
--查詢員工號,姓名,月薪,獎金,年薪 -- 需要注意別名,如果不使用雙引號,不能有空格和不可見字符
SQL> select empno as "員工號",ename "姓 名",sal 薪水,comm 獎金,sal*14 年薪 from emp;
3).select * 與 select 列的區別
推薦寫指定列,效率高.
4).null(空,也就是項目里面沒有內容)與表達式運算,結果為null,為防止結果為空,則
使用濾空函數:nvl(a,b),含義是如果a為null則結果為b,如果a不為null則結果為a
--查詢員工號,姓名,月薪,獎金,年薪,年收入 -null搗蛋鬼
null與表達式運算,結果為null ,使用nvl函數慮空函數 nvl(a,b) 如果a為null,結果為b,如果a不為null,結果為a
SQL> select empno as "員工號",ename "姓 名",sal 薪水,comm 獎金,sal*14 年薪,sal*14+nvl(comm,0) from emp;
5).使用distinct關鍵字去除重復行,
--查看員工表不同的部門編號distinct關鍵字,作用域:整行
SQL> select distinct deptno from emp;//得到不同的部門編號
注意,distinct作用域是整行,而不是某一列,
比如select distinct deptno,job from emp;
這是得到的是deptno和job組合起來不同的結果,也就是整體(行)不同的結果,而不是單獨deptno列或者job列不同的結果
6).sql錯誤修改方法:
出錯時使用edit或者ed指令,這是會彈出一個記事本,修改記事本中的內容也就是上一條指令,然后保存退出,再次使用/來執行上一條指令即可,這樣便於修改比較長的指令
--sql錯誤修改方法
ed(it) 指令
7).sql也可以來計算表達式以及顯示當前日期等,
--輸出計算表達式 3+20*5,顯示當前日期
SQL> select 3+20*5, sysdate from emp;//這個就會有結果顯示,但是顯示的結果是很多行的,行數等於emp中的行數。
//如何解決這個多行的問題,就要使用到偽表(即空表)和偽列(是本來就存在的列,但不依賴於某一個表),即dual(計算或者函數情況時使用),
偽表和偽列dual (計算或者函數情況時使用) ,偽列是本來就存在的列,不依賴於某一個表
SQL> select 3+20*5, sysdate from dual;
8).SQL語句使用注意事項
SQL 語言大小寫不敏感。
SQL 可以寫在一行或者多行
關鍵字不能被縮寫也不能分行
各子句一般要分行寫。
使用縮進提高語句的可讀性。
9).連接符
Oracle中定義了一個連接符 ’||’ 用來連接字符串。
顯示“xxx 是一個 xxx”怎么顯示呢?
SQL> select ename || ‘ is a ’ || ‘job’ from emp
但是如果顯示一個“hello world”應該怎么顯示呢?
(1).使用concat函數來實現:SQL> select concat(‘hello’, ‘ world’) from ????
這兩個字符串不是任何一個表的內容,在Oracle中,定義一張“偽表”dual用來滿足SQL99語法(ANSI)。
語法規定:select 后 必須接 from。
可以這樣:SQL> select concat('aa', concat('bb', 'cc')) from dual;
(2).使用連接符 || 來實現: SQL> select 'hello' || ' world' || ' Oracle' as "你好" from dual
再如: SQL> select 3 + 2 from dual;
‘||’就是用來代替concat函數的。SQL> select ename || ‘ is a ’ job “員工描述” from emp
10).SQL和sqlplus
sqlplus是登錄工具
sql命令是敲出來,
如何區別,注意操作本地的就是屬於sqlplus的命令,也就是說在客戶端執行的命令就是sqlplus的命令,如果是對oracle實例進行操作的那就是sql的命令。
也就是說,sql指令是需要和數據庫進行交互的,sqlplus是本地更改
比如:
desc ql
edit sqlplus
select sql
那么sqlplus有哪些指令呢,可以在sqlplus下執行 ? topic即可。
區分的意義在於,如果換了數據庫那么sqlplus的命令就不一樣了,因為客戶端是其他數據庫的了,但是sql指令是一樣的。
注意,
sql指令可以換行寫,但是第一行不能缺少select
/*******************************************************************************************/
四、where過濾
where過濾條件
基本語法 select ... from ... where condl
其中這個條件就涉及到運算符
其中,比較運算符要注意<>就是不等的意思 between and是一對整體
1.比較運算符 = != <> < > <= >= between and
--查詢10號部門的員工信息
SQL> select * from emp where deptno=10;
--查詢員工名字為king的員工信息
SQL> select * from emp where ename='KING';//注意在sql中單引號代表字符串,雙引號代表別名,注意字符串要全匹配也就是說區分大小寫
--查找薪水不等於1250員工的信息
SQL> select * from emp where sal != 1250;
SQL> select * from emp where sal <> 1250;
--查詢入職日期為1981年11月17日的員工信息---日期型的查詢
注意日期型的查詢,也就是如何匹配日期,注意日期格式敏感
首先查看數據庫的參數配置,也就是查看日期格式的設置:select * from v$nls_parameters;
然后查詢的時候比較的字符串格式要與設置的一致
或者設置日期格式:alter session set NLS_DATE_FORMAT='yyyy-mm-dd';,然后按照該格式輸入字符串查詢,注意登錄的是會話所以設置的是所屬於會話的。
所以如果有人修改了設置,查詢會報錯。所以一般不會輕易去改動
SQL> select * from emp where hiredate='1981-11-17';
select * from emp where hiredate='1981-11-17'
*
第 1 行出現錯誤:
ORA-01861: 文字與格式字符串不匹配
--查看日期設置 select * from v$nls_parameters; year-月份-日期 yyyy-mm-dd
見圖1:
SQL> select * from emp where hiredate='17-11月-81';--日期格式敏感
SQL> alter session set NLS_DATE_FORMAT='yyyy-mm-dd';
SQL> select * from emp where hiredate ='1981-11-17';
帶來一個問題,如果有人修改了設置了,我們的語句將報錯!!!
SQL> alter session set NLS_DATE_FORMAT='dd-mon-rr';--恢復原狀
2.前面都是一個條件,如果需要多個條件的就需要用到邏輯運算符:邏輯運算符 and or not
--查詢10號部門或者20部門的員工信息
SQL> select * from emp where deptno =10 or deptno = 20;
--查詢10號部門員工工資為1300的員工信息
SQL> select * from emp where deptno =10 and sal=1300;
--查找工資介於1000-2000之間的員工信息,
SQL> select * from emp where sal >=1000 and sal <= 2000;
等同於:SQL> select * from emp where sal between 1000 and 2000 ;
注意,between 1000 and 2000這個是閉區間,即[]
--查詢81年2月(含2月)至82年2月(不含2月)入職的員工信息81-2-1 -->82-1-31
SQL>select * from emp where hiredate between '1981-2-1' and '1982-1-31';
--查詢獎金為空的員工信息-null
注意,查詢為空的記錄時,也就是null時,要注意,
null重要結論,不能用= !=,也就是說 columnname=null 或者!=null 均永遠為假
正確的寫法:SQL>select *from emp where comm is null;
或者SQL>select *from emp where comm is not null;
--多個條件時怎么寫更優?
注意,sql查詢記錄的時候,是讀取一條條記錄然后和where條件進行比對,所以查詢語句越少效率越高。
當一條查詢語句中有多個條件時,這時sql是從where后面最右邊一個條件開始比對,直到where最近的一個條件。所以為了效率最高,如果最后的條件就能(退出比對)直接返回
結果,那么就是最優的寫法,(and的時候,易假的放在右側,or 與之相反.)
--多個邏輯運算符的執行順序
注意出現多個邏輯運算符的執行順序,也就是優先級,從前面我們可以得到從右邊開始比對,所以邏輯運算是從右開始執行,
當一個結果計算好后再由右向左與下一個進行運算。如果想打破這種順序,就需要用()將先執行的包括起來:
SQL>select * from emp where (deptno=10 or deptno=30) and sal=1250
3.in:在集合中
--查詢部門號是10或者20的員工信息:
SQL>select * from emp where deptno in (10,20);
--查詢部門號不是10或者20的員工信息:
SQL>select * from emp where deptno not in (10,20);
注意,如果想查詢部門號不是10或者20也不為空的員工信息:
select * from emp where deptno not in (10,20,null);
這樣是錯誤的,因為null不能用來進行判斷的,否則為假,這個前面也說了這個重要結論了
所以結論是 :not in 的集合不能有null
4.like:模糊查詢
知識點: ‘%’匹配任意多個字符。‘_’匹配一個字符
--查詢員工首字母是S的員工信息
SQL>select * from emp where ename like 'S%';
--查詢四個字母的員工信息
SQL>select * from emp where ename like '____';//四個下划線即可
--查詢帶有下划線的,涉及到轉義,要使用關鍵字escape
SQL>select * from emp where ename like '%\_%' escape'\';//把_轉義為普通字符,注意escape用來定義轉義字符
SQL>select * from emp where ename like '%/_%' escape '/';
select ... 指定列
from ... 指定表
where... 指定條件
作用是為了實例可以解析sql命令
null總結:
1.null在表達式結果為null ,要用慮空函數nvl(a,b) 如果a為null,結果為b,否則結果為a
2.not in 的集合中不能有null
3.null 不能用= ,!= (<>)
/*******************************************************************************************/
五、排序
完整sql語句
select...
from...
where...
group by...
having...
order by...
排序可以 列名,表達式,別名,序號,語法是order by col|alias|number 默認是asc模式,升序,desc降序
其中,這個序號的意思是屬於查詢結果集中第幾個的意思,如果查詢結果集是*那么序號就是第幾列了。
例如:
1.
--員工信息按入職日期排序
SQL>select * from emp order by hiredate;
2.排序(序號)
--員工薪水按從大到小排序(序號)
SQL>select * from emp order by sal desc;
SQL>select * from emp order by 6 desc;//第6列降序排序,6等價於sal
SQL>select empno,ename,sal from emp order by 3 desc;//等價於前面的,還是按照第6列(sal)降序排序
3.多種條件排序
--員工信息按部門、薪水排列
//兩種或多種條件排序,條件越靠前面的越優先進行排序,即當前面排序條件相同時再用后面的條件排序,即
排序的原則:先按第一個字段,然后再按第二個字段...
SQL>select * from emp order by deptno,sal;//先按deptno排序,deptno中一樣的再按sal排序。
4.作用范圍
--asc 和desc 作用范圍是它之前的一個字段
SQL>select * from emp order by deptno,sal desc;//desc只作用於sal,即sal是按遞減排序的。
5.null 代表無窮大
--員工信息按獎金逆序 (nulls last --null)
null 代表無窮大,排序默認排在最后,
如果在降序中則在最前面,要想把null的放在最后則要在語句后面加上nulls last,強制放到最后
或者使用濾空函數把空的變為最小再排序:
SQL>select * from emp order by nvl(comm,-1) desc;//可能數據庫的數據里有0的,所以填-1來保證最小(一旦comm為null就等於-1
)。
/*******************************************************************************************/
六、單行函數
單行函數:只對一行進行變換,產生一個結果。函數可以沒有參數,但必須要有返回值。如:concat、nvl
操作數據對象
接受參數返回一個結果
只對一行進行變換
每行返回一個結果
可以轉換數據類型
可以嵌套
參數可以是一列或一個值
1.字符函數
--lower小寫,upper大寫,initcap首字母大寫
SQL>select lower('Hello wOrld') 一,upper('Hello wOrld') 二,initcap('Hello wOrld') 三 from dual;
concat(連接符||),substr,length,lengthb,instr
--concat函數,字符串連接,只能傳入兩個參數,多個字符連接可以使用concat中嵌套concat實現,或者使用這個符號: ||,這個對於個數沒有限制。
SQL>select concat('hello',' world') from dual;
SQL>select 'hello'||' world'|| ' 1111'||'2222' from dual;
--length求字符長度,注意漢字當一個字符, lengthb求字節數,一個漢字占兩個字節。
SQL> select length('hello中國'),lengthb('hello中國') from dual;
LENGTH('HELLO中國') LENGTHB('HELLO中國') ---需要注意lengthb求字節數,gbk編碼漢字2個字節
---------------------------------------
7 9
--substr
SQL>select substr('helloworld',1,3) 一,substr('helloworld',1) 二 ,substr('helloworld',-3) 三 from dual;
--instr函數返回的是子字符串所在的位置,位置從1開始,若不存在這樣的子字符則返回0
SQL>select instr('helloworld','owo') 一,instr('helloworld','owow') 二 from dual;--查找instr(a,b) 判斷b是否在a中,如果存在,返回b在a中的位置,如果不存在返回0
--l(r)pad(str,length,C)
返回length長度的字符串,如果str不夠,則在左(右)補充C字符
lpad有三個參數,第一個參數為字符串,第二個參數為長度,第三個為字符,意思是返回滿足第二參數表示的長度的
字符串(是第一個參數中的),不夠則用第三個參數在左邊補全。
rpad有三個參數,第一個參數為字符串,第二個參數為長度,第三個為字符,意思是返回滿足第二參數表示的長度的
字符串(是第一個參數中的),不夠則用第三個參數在右邊補全。
--trim函數,
trim函數是去空格的,去掉字符串左邊和右邊的空格,字符串中間的空格是不會去的.如果不是去掉指定字符串的首尾的空格,
而是去掉字符串首尾的其他字符,則原先的寫法由trim(' 字符串 ')改為trim('字符' from '字符串')
SQL>select 'aaa'||trim(' hello world ')||'bbb' from dual;
SQL>select trim('H' from 'HHHHHelloHHHworldHHHHH') from dual;--trim(C from str) 去掉str首尾為C的字符
--replace函數,
SQL>select replace('helloworld','llo','kk') from dual;
SQL>select replace('helloworld','llo','') from dual;
//表示的是將helloword中的llo替換為kk,如果kk不寫得話就相當於去掉了其中的llo
2.數值函數
數值函數 正、負表示小數點之后,或小數點以前的位數
--round(a,b)函數:四舍五入,
SQL> select round(45.926, 2) 一, round(45.926, 1) 二, round(45.926, 0) 三, round(45.926, -1) 四, round(45.926, -2) 五 from dual;
--trunc(a,b)函數:直接舍掉,
其中b為正數表示取小數點后b位,為負數表示取小數點前b位。
SQL> select trunc(45.926, 2) 一, trunc(45.926, 1) 二, trunc(45.926, 0) 三, trunc(45.926, -1) 四, trunc(45.926, -2) 五 from dual;
--mod(a,b),取余,a除以b的余數
SQL> select mod(1600,600),mod(600,1600) from dual;
MOD(1600,600) MOD(600,1600)
--------------------------
400 600
--ceil(a/b) ,a除以b商向上取整。floor(a/b) ,a除以b商向下取整
SQL> select ceil(121/60),floor(121/60) from dual;
3.轉換函數
(1).顯示轉換,就是使用函數轉換,
函數包括 to_char to_number to_data,注意這字符,數字,日期這三種類型之間的轉換,
字符是可以和其他互相轉換的,但是數字和日期之間無法直接轉換,也就是說字符串起中間作用,具體見圖2:
(2).隱式轉換就是用等號就轉換了類型
具體見圖3:
隱式轉換前提:能轉換。
當能夠顯示轉換的時候盡量顯示轉換.
(3).轉換函數的使用格式:
字符函數(待轉換內容,轉換的格式)
其中轉換的格式都是可逆的。
--to_char 與to_number互相轉換
注意to_char函數中使用到的格式,
例如:
--把薪水轉換為本地貨幣字符型,見圖4:
SQL> select sal,to_char(sal,'L9,999') from emp;//表示將原來數值型的薪水都轉換為帶有貨幣符號和四位數值
以及千位符這種格式的字符型
SQL> select to_number('¥1,250','L9,999') from dual;//轉換回去,轉換的格式還是一樣的
TO_NUMBER('¥1,250','L9,999')
-----------------------------
1250
--to_char和to_date互相轉換
注意to_date函數中使用到的格式,具體見圖5:
例如查找日期型數據:
SQL> select * from emp where hiredate=to_date('1981-11-17','yyyy-mm-dd');//將字符類型按照日期類型轉換后用作條件查找
to_char 與 to_date 顯示 "yyyy-mm-dd hh24:mi:ss 今天是 星期幾"
SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss "今天是" day') from dual;
//其中,格式化字符串中的非格式化字符用雙引號,系統時間所在的偽列為sysdate,
將上述輸出字符串反轉回日期
select to_date('2017-02-13 16:06:04 今天是 星期一','yyyy-mm-dd hh24:mi:ss "今天是" day') from dual;
4.日期函數
(1).顯示昨天,今天,明天
SQL> select sysdate-1 昨天,sysdate-1 今天,sysdate-1 明天 from dual;//oracl日期格式可以加數字。注意日期之間可以
相減表示天數,但是不能相加。//其中昨天,今天,明天是列的別名,也就是顯示出來的列名用逗號后面的別名不用前面的
--計算員工工齡 可以按日,周,月,年 日期差減方法
SQL> select sysdate-hiredate 日,(sysdate-hiredate)/7 周,(sysdate-hiredate)/30 月,(sysdate-hiredate)/365 年 from emp;
(2).日期函數 mounts_between(a,b) add_months
參數類型是日期類型,表示a和b之間間隔幾個月。sql中的乘除都是可以帶小數的,這個函數如果結果不是整數返回的也是小數。
用這種函數算比自己用天數來除以12更准確。
SQL> select months_between(sysdate,to_date('2016-12-13','yyyy-mm-dd')) from dual;
SQL> select months_between(sysdate,hiredate) 月,months_between(sysdate,hiredate)/12 年 from emp;
(3).日期函數 last_day
求給定月份的最后一天
SQL> select last_day(sysdate) from dual;
(4).日期函數 next_day
求給定日期的下一個星期幾
SQL> select next_day(sysdate,'星期六') from dual;//求當前日期的下一個星期六
/*******************************************************************************************/
七、條件表達式
sql中沒有if else,所以判斷條件一個用的是case...end,還有一個是decode
1.case...end -------sql99標准
語法為:
CASE expr WHEN comparison_expr1THEN return_expr1
[WHENcomparison_expr2 THENreturn_expr2
WHENcomparison_exprn THENreturn_exprn
ELSE else_expr]
END
case 變量 when 變量值 then 該變量值下要做的動作
(when 變量值 then 該變量值下要做的動作)
(else 其他條件要做的動作)
end
示例:
SQL> select empno,ename,job,sal 漲前薪水,case job when 'PRESIDENT' then sal+0
when 'MANAGER' then sal+200
else sal+1000 end 漲后薪水
from emp;
2.decode函數 ------oracle特有
decode:是一個函數,其語法為:
DECODE(col|expression, search1, result1
[, search2, result2,...,]
[, default])
語法中文說明:
decode(變量,變量值,該變量值下要做的動作,變量值,該變量值下要做的動作,其他條件要做的動作 );
即參數位置含義固定
示例:
SQL> select empno,ename,job,sal 漲前薪水,decode(job,'PRESIDENT',sal+1000,'MANAGER',sal+800,sal+400) 漲后薪水
from emp
/*******************************************************************************************/
八、分組多行(函數)與分組數據
1.分組函數
分組函數是針對前面單行函數說的,前面學的函數都是單行函數。多行函數也叫組函數
即分組函數是對多行進行操作的,作用的范圍是多行的。
分組函數包括:max,min,avg,sum,count(求數量),
1).sum(求和)
--求員工的工資總額
SQL> select sum(sal) from emp;
2).count(求數量)
--員工人數
SQL> select count(*) from emp;//求員工人數,即行數
--求emp表中的工種數
SQL> select count(distinct job) from emp;//求工種數目,即去除了重復
3).avg(求平均)
--求員工的平均工資(兩種方式)
SQL> select avg(sal) 一,sum(sal)/count(*) 二 from emp;
--求員工的平均獎金(三種方式)
SQL> select avg(comm) 一,sum(comm)/count(*) 二,sum(comm)/count(comm) 三 from emp;
一 二 三
------------------------------
550 157.142857 550
結果:只有第一個和第三個一樣。
原因是,count(*)計算的是行數即獎金不管是否為空都計算,count(comm)計算的是獎金不為空的數目
即組函數都有自動的濾空功能。
2.分組數據
分組數據的含義就是把數據進行分組,然后按組進行處理,使用GROUP BY 子句數據分組:
按照group by 后給定的表達式,將from后面的table進行分組。針對每一組,使用組函數(分組數據的含義就是把分出來的各個組的數據一起顯示出來)。
group by中出現的也必須在在select中出現,這樣才能選中顯示出來(在select中出現的非組函數的列則必須有分組即group by...,
同時在select中出現的非組函數的列,必須在group by中出現.)
示例:
統計各個部門的平均工資,先統計10號的,再統計20的,最后統計30的
SQL> select deptno,avg(sal) from emp group by deptno;//按部門號計算每個部門的平均工資,即按部門號分組,一號一組,再計算每組的平均工資,(再按部門號列出來)
SQL> select a,avg(X) from tablename group by a
SQL> select a,b,avg(X) from tablename group by a,b
SQL> select a,b,c,avg(X) from tablename group by a,b,c
...
結論:(首先出現a,avg(x)則必須有分組即group by...,同時在select中出現的非組函數的列,必須在group by中出現)。
--查詢各部門平均工資
SQL> select deptno,avg(sal) from emp group by deptno
--查詢各部門各工種平均工資
SQL> select deptno,job,avg(sal) from emp group by deptno,job;
3.having
使用HAVING 子句過濾分組結果集。
使用 HAVING 過濾分組:
1). 行已經被分組。
2). 使用了組函數。
3). 滿足HAVING 子句中條件的分組將被顯示。
where后不能使用分組函數. having 的作用對分組進行過濾
--查詢平均薪水大於2000的部門
SQL> select deptno,avg(sal) from emp group by deptno having avg(sal)>2000;
--求10號部門員工的平均薪水
select deptno,avg(sal) from emp group by deptno having deptno=10;
select deptno,avg(sal) from emp where deptno=10 group by deptno;
having與where哪個好?
如果都能用的情況下,優先使用where,因為先過濾再分組(sql語句是順序執行的)效率更高。
4.常見的非法使用組函數的情況
--缺失group by字句,語法不符合規范.
//
select查詢可以全部列,部分列,表達式,別名
group by分組的要求:在select中出現的非組函數的列,必須在group by后出現,--數據按什么分組,數據如何顯示?。
分組函數也叫組函數,或者 聚合函數
like效率比較低,能少用就少用。
/*******************************************************************************************/
九、多表查詢(基於oracle)
1.理論基礎:笛卡爾全集
笛卡爾全集:兩個表中的每條記錄兩兩組合形成新的表,
所以新的表的列數等於兩個表列數相加,新的表的行數等於兩個表行數相乘:
全集的行數= 表1 的行數* 表2的行數
全集的列數= 表1的列數+ 表2的列數
組合后還要看新的表中的記錄是否有意義,即兩個表中相同的列中的元素相等的記錄才有意義,即連接條件(相同的列)相等的記錄才有意義
沒有意義的是垃圾數據,需要使用連接條件過濾,如下圖6,圖7 中畫下划線的部分就是垃圾數據:
2張表連接的條件至少是1個
3張表連接的條件至少是2個
===>N張表的連接條件至少是N-1
2.連接條件寫法:1.表名.列名 2.給表起別名 別名.列名
推薦用第二種,因為當表名很長的時候 別名更方便。
例子:
select * from emp,dept;//emp,dept就是卡爾全集:
select * from emp e,dept d where e.deptno=d.deptno;//e d是別名,連接條件是deptno相等的
3.根據連接條件的不同可以划分為:等值連接,不等值連接,外連接,自連接
Oracle 連接:
Equijoin:等值連接
Non-equijoin:不等值連接
Outer join:外連接
Self join:自連接
SQL: 1999
Cross joins
Natural joins
Using clause
Full or two sided outer joins
1).等值連接(使用等號,在mysql中稱為內連接)
--查詢員工信息:員工號 姓名 月薪(emp)和部門名稱dept
SQL> select empno,ename,sal,dname from emp e, dept d where e.deptno = d.deptno ;
注意,都有的列不能放在select后面,這樣會有沖突,必須要指定時屬於哪張表的列,放在連接條件內其實就不用指定了
2).不等值連接(不使用等號)
--查詢員工信息:員工號 姓名 月薪 和 薪水級別(salgrade表)
SQL> select empno,ename,sal,grade
from emp e,salgrade s
where e.sal>=s.losal and e.sal<=s.hisal;
--可以用between and
SQL> select empno,ename,sal,grade
from emp e,salgrade s
where e.sal between s.losal and s.hisal;
注意,當前薪水符合范圍的,這樣的記錄中的薪水級別才是對的。
也就是說,記錄要符合連接條件,也就是經過連接條件過濾后的記錄是要有意義的。
3).外連接
當連接條件中的內容兩個表不一致時,或者說連接重組后的表中,有的內容是只有一張表中有的,其他的表沒有,如圖8中的40號部門這一行:
這時如果需要統計這條記錄時,則要特別處理.
例子:
--按部門統計員工人數,顯示如下信息:部門號 部門名稱 人數,注意有的部門沒有人
SQL> select d.deptno,d.dname,count(empno)//count(empno)統計有人的部門,所以不能用*。由於不知道用哪一列,以及不知道列的別名是什么,所以這里最后寫
from dept d,emp e
where e.deptno(+)=d.deptno//需要包含那條數據不完整的記錄,也就是想保留等號右邊的,(+)放在等號左邊,稱為右外連接
group by d.deptno,d.dname;
--左外連接:想保留等號左邊的,(+)放在右邊
SQL> select d.deptno,d.dname,count(empno)
from dept d,emp e
where d.deptno=e.deptno(+)
group by d.deptno,d.dname
order by 1
--右外連接:想保留等號右邊的,(+)放在左邊
SQL> select d.deptno,d.dname,count(empno)
from dept d,emp e
where e.deptno(+)=d.deptno
group by d.deptno,d.dname
order by 1
4).自連接
一個表中的兩條記錄需要組合起來或者說需要相連接起來,這時就需要自連接,也就是把一張表當作兩張表.
所以自連接時特殊的外連接。
例子:
--查詢員工信息:xxx的老板是yyy
分析:可以把員工表emp當成兩個表,一個是員工表,一個是老板表。
SQL> select e.ename||'''s boss is' || nvl(b.ename,'himself')//字符串內部還需要顯示單引號,則直接加單引號即可
from emp e,emp b
where e.magr=b.empno(+)
//老板沒有上級,所以這個條件不成立,但是為了把這條不成立的記錄保留下來,也就是需要保留這條不完整的記錄,這條記錄
只有左邊的,即只有左邊的自己沒有老板,所以保留左邊的。
--下面的查詢結果少了大老板,因為連接條件不成立
select e.ename||'''s boss is'||b.ename
from emp e,emp b
where e.mgr = b.empno
自連接的使用條件:
(1).數據都在一個表中
(2).數據不在同一行
自連接的弊端:
自連接對應的笛卡爾集合是平方的關系,所以行數很多(數量級平方的增長).所以最好在小表中使用
/*******************************************************************************************/
十、子查詢
什么情況下使用子查詢?一個查詢不能完成的時候。
子查詢的本質:sql嵌套sql
例子:
查詢比scott工資更高的員工信息
分析:首先找到scott的工資,然后條件大於這個工資的。
SQL> select * from emp where sal>(select sal from emp where ename='scott');
注意事項:
1. 合理的書寫風格 (如上例,當寫一個較復雜的子查詢的時候,要合理的添加換行、縮進)
2. 小括號( )
3. 主查詢和子查詢可以是不同表,只要子查詢返回的結果主查詢可以使用即可
--查詢部門名稱是“SALES”的員工信息
分析: 1. 先找到 SALES 部門名稱對應的編號 2. 用這個編號做條件過濾員工
select deptno from dept where dname ='SALES';
select * from emp where deptno = 30;
===>
SQL> select * from emp where deptno = (select deptno from dept where dname ='SALES');
4. 可以在主查詢的where,select,having,from后都可以放置子查詢,即"兩個by"后面不行。
select ..
from …
where …
group by … err
having …
order by … err
1).select后
--查詢10號部門員工號,員工姓名,部門編號,部門名稱
(1).等值連接寫法
SQL> select e.empno,e.ename,e.deptno,d.dname
from emp e,dept d
where e.deptno=d.deptno and e.deptno=10;
(2).子查詢寫法
SQL> select empno,ename,deptno,(select dname from dept where deptno=10)
from emp
where deptno=10;
2).from后
--查詢員工的姓名,薪水和年薪
SQL> select * from (select empno,ename,sal*14 from emp);
3).where后
--查詢與ward相同job並且薪水比他高的員工信息
先找到ward的工種和薪水,然后用作過濾條件
select sal,job from emp where ename ='WARD';
select * from emp where job='SALESMAN' and sal > 1250;
==>變成子查詢
SQL> select * from emp where job=(select job from emp where ename ='WARD') and sal > (select sal from emp
where ename ='WARD');
4).having后
--查詢高於30號部門最低薪水的部門及其最低薪水
SQL> select deptno, min(sal)
from emp
group by deptno
having min(sal) > (select min(sal) from emp where deptno = 30)
5. 不可以在主查詢的group by后面放置子查詢 (SQL語句的語法規范)
6. 強調:在from后面放置的子查詢(***) from后面放置是一個集合(表、查詢結果)
7. 單行子查詢只能使用單行操作符;多行子查詢只能使用多行操作符
單行的查詢只能使用返回結果是單行的子查詢(單行子查詢就是該條子查詢執行結束時,只返回一條記錄(一行數據)),
即整個查詢語句是單行的則括號里面返回的結果也要是單行的。多行子查詢(子查詢的結果是多行的)
這個規范的意思也就是說,結果要能組合起來。//其實主要意思是多行操作符操作的多行,所以多行操作符后面得是多行的集合,也就是多行子查詢(子查詢的結果是多行的集合)
單行操作符:
=、>、>=、<、<=、<>
多行操作符有:
IN 等於列表中的任意一個
ANY 和子查詢返回的任意一個值比較
ALL 和子查詢返回的所有值比較
舉例:
1).多行操作符 IN,in后面為多行子查詢(子查詢的結果是多行的)
--查詢部門名稱為SALES和ACCOUNTING的員工信息
分析: 先找到部門編號,再通過編號查找
select * from dept where dname in ('SALES','ACCOUNTING');
select * from emp where deptno in (10,30);
==> 變成子查詢
SQL> select * from emp where deptno in (select deptno from dept where dname in
('SALES','ACCOUNTING'));
2).多行操作符 ANY,any后面為多行子查詢(子查詢的結果是多行的)
--查詢薪水比30號部門任意一個(any 某一個,即薪水最低的)員工高的員工信息
--多行子查詢寫法,any后面為多行子查詢(子查詢的結果是多行的)
SQL> select * from emp where sal > any (select sal from emp where deptno=30);
--單行子查詢寫法
SQL> select * from emp where sal > (select min(sal) from emp where deptno=30);
3).多行操作符 ALL ,all后面為多行子查詢(子查詢的結果是多行的)
--查詢薪水比30號部門所有人工資高的員工信息
--多行子查詢寫法,all后面為多行子查詢(子查詢的結果是多行的)
SQL> select * from emp where sal > all (select sal from emp where deptno=30);
--單行子查詢寫法
SQL> select * from emp where sal > (select max(sal) from emp where deptno=30);
8. 子查詢中的null值
判斷一個值等於、不等於空,不能使用=和!=號,而應該使用is 和 not
例子:
--查詢不是老板的員工信息
思路 先找到誰是老板,取反
--得到老板
select * from emp where empno in( select mgr from emp);
--取反
SQL> select * from emp where empno not in( select mgr from emp where mgr is not null);//not in里面不能出現有空值,一旦有,那么結果始終為假
9. 一般先執行子查詢(內查詢),再執行主查詢(外查詢);但是相關子查詢除外
10. 一般不在子查詢中使用order by, 但在Top-N分析問題中,必須使用order by
/*******************************************************************************************/
十一、配置與配置文件(oracle)
登陸命令sqlplus scott/11@orcl222中的orcl222來自於
配置文件tnsnames.ora,在配置文件中成為服務名
增加新的登陸服務,可以參考該文件中的其他登陸服務來填寫服務名,ip,端口號,實例名
也可以使用工具來完成這個增加的操作 :
工具名稱:oracle net configuration assistant
注意,
登陸數據庫的工具:pl/sql developer 中的drop命令表示的是刪除的意思 。
/*******************************************************************************************/
十二、集合運算
1.集合運算符
集合運算的操作符:
1).A並B 並集 對應sql關鍵字:union(集合的交集只保留一份), union all(兩個集合的內容全部保留)
SQL> select * from emp where deptno in(10,20) union select * from emp where deptno in(20,30)
SQL> select * from emp where deptno in(10,20) union all select * from emp where deptno in(20,30)
2).A交B 交集 對應sql關鍵字: intersect
SQL> select * from emp where deptno in(10,20) intersect select * from emp where deptno in(20,30)
3).A-B 差集 對應sql關鍵字: minus
SQL> select * from emp where deptno in(10,20) minus select * from emp where deptno in(20,30)
(from后面放的是一個集合(表))
具體見圖9
2.集合運算需要注意的問題:
1).參與運算的各個集合必須列數相同,且類型一致。注意如果不一致則要處理,一般以列數最多的集合為准,
其他集合相對這個列數最多的結合用null來補充對應的列,注意列的類型要一致。
2).采用第一個集合的表頭作為最終使用的表頭。 別名也只能在第一個集合上起
3).可以使用括號先執行其中的語句。
3.示例,按照圖10顯示的樣式輸出結果:
分析:
數據分成三塊;
1). 各個部門各個工種的工資總額 集合
2). 各個部門的工資總額
3). 總額
求集合
SQL> select deptno,job,sum(sal) from emp group by deptno,job union
SQL> select deptno,to_char(null),sum(sal) from emp group by deptno union
SQL> select to_number(null),to_char(null),sum(sal) from emp;
SQL> break on deptno skip 2;//設置為 去掉相同的部門號兩個,這個是報表的處理技巧
SQL> break on null;//恢復回去 break on null
/*******************************************************************************************/
十三、數據處理
SQL 語言類型:
DML data manlpulation language 數據操縱語言: insert,select,update,delete,所有的dml語句中都可以使用子查詢,同時子查詢的規則同樣適用
DDL data definition language 數據定義語言: create,truncate
DCL data control language 數據控制語言: grant(賦權)/revoke(回收權限)
1.insert插入數據
語法格式:
INSERT INTO table[(column[,colum...])] VALUES (value{,value...});
value 與 column (數目)要對應
1).可以插入全部列,即所有的列都有新數據的插入,此時不用指明列名
insert into dept values(50,'50name','50loc');
2).插入部分列
insert into dept(deptno,dname) values(51,'51name');//隱式插入Null
insert into dept(deptno,dname,loc) values(51,'51name',null);//顯式插入Null
3).地址符號 & 的作用
& 號會提示用戶來輸入內容,用於插入
insert into dept values(&dpt,&dnm,&loc);
如圖11:
這樣的好處在於:
使用 / 來執行上一條語句(&號的語句),則直接提示用戶輸入數據,這樣就不用輸入指令就可以插入數據了,
加快了插入數據的速度,
同時還可以用格式化輸入,比如加入引號后,則用戶輸入就不需要再輸入引號了:
insert into dept values(&dpt,'&dnm','&loc');
4).批量新增數據
create table emp10 as select * from emp where 1=2;
復制emp表的結構,由於1=2條件不會成立,所以表的數據不會被復制
批量插入10號部門的信息
insert into emp10 select * from emp where deptno=10;//將查詢返回的結果插入到新表中。
5).insert也可以使用選擇一個集合的方式來插入
insert into dept select 57,'57name','57loc' from dual;
其中 表示選中 列的內容分別為 的記錄(特定的一條記錄)
從偽表中去內容等同於直接輸入指。
即等同於
insert into dept values(57,'57name','57loc');
2.update 更新數據
語法格式:
update tablename set column1=val1,column2=val2,... where cond;
對於更新操作來說,一般會有一個where條件,如果沒有這個限制條件,更新的就是整張表.
例子:
update dept set loc ='51loc',dname='51dname' where deptno=51;
update dept set loc ='51loc',dname='51dname' where deptno=&dpt;//由用戶輸入來決定該得的是哪個編號的行
update dept set dname='52name',loc=null where deptno=52;//set中的等值可以有null
子查詢的規則和注意事項在DML語句都適用!!
3.delete 刪除數據
1).語法格式:
delete from tablename where cond...;
2).例子:
delete from dept where deptno=52;
3).注意:
做事情之前要做好備份:
批量新增就是備份了或者用客戶端工具拷貝到excl(可以拷貝回來)。
4).delete 刪除的是表內容,不會刪除表結構,//刪除表:drop命令
注意,刪除一條記錄后,后面的記錄將會移動到這條記錄的位置上。
如果說刪除的記錄還想插回的是原來的位置,則需要開啟行移動。
5).delete 和truncate
delete和turncat兩者都可以刪除數據
(1).delete 是逐條刪除數據,truncate 是先摧毀表,再重建. 表的數據會被清空 ..
刪除整個表時,效果都一樣,但是:
當前數量級的情況下,delete更快,當數據量大(比如一百萬條記錄時)的時候,truncate更快
所以工作的時候,還是用turncate。
(由於delete使用頻繁,Oracle對delete優化后delete快於truncate,即delete更快的原因是oracle對delete進行了優化,但是mysql里面實際上是truncate更快)
(2).delete 是DML語句,truncate 是DDL語句。
DML語句可以閃回(flashback),DDL語句不可以閃回(回滾)。
(閃回: 做錯了一個操作並且commit了,對應的撤銷行為。了解)
(3).由於delete是逐條操作數據,所以delete會產生碎片,truncate不會產生碎片。
(同樣是由於Oracle對delete進行了優化,讓delete不產生碎片)。
兩個數據之間的數據被刪除,刪除的數據——碎片,整理碎片,數據連續,行移動 【圖示】
(4).delete不會釋放空間,truncate 會釋放空間
用delete刪除一張10M的表,空間不會釋放。而truncate會。所以當確定表不再使用,應truncate
(5).delete可以回滾rollback, truncate不可以回滾rollback。
(6).【做實驗sql.sql】:驗證delete和truncate的時效性
關閉時間開關就不會顯示: 已用時間:xxxx
關閉回顯開關就不會顯示: 已選擇x行
@符號代表要執行哪個腳本
語句執行時間記錄開關:set timing on/off
回顯開關:set feedback on/off
示例:
SQL> set feedback off
SQL> set timing off
SQL> @c:\sql.sql
SQL> select count(*) from testdelete;
COUNT(*)
----------
5000
SQL> delete from testdelete;
SQL> rollback;
SQL> select count(*) from testdelete;
COUNT(*)
----------
5000
SQL> set timing on
SQL> set feedback on
SQL> delete from testdelete;
已刪除5000行。
已用時間: 00: 00: 00.03
SQL> select count(*) from testdelete;
COUNT(*)
----------
0
SQL> set feedback off
SQL> set timing off
SQL> @c:\sql.sql
SQL> select count(*) from testdelete;
COUNT(*)
----------
5000
SQL> set timing n
SP2-0265: timing 必須設置為 ON 或 OFF
SQL> set timing on
SQL> set feedback on
SQL> truncate table testdelete;
表被截斷。
已用時間: 00: 00: 00.31
SQL> select count(*) from testdelete;
COUNT(*)
----------
0
已選擇 1 行。
結論:當前數量級的情況下,delete更快,當數據量大(比如一百萬條記錄時)的時候,truncate更快
所以工作的時候,還是用turncate。
/*******************************************************************************************/
十四、事務
1.事務:
若干個操作指令的集合(DML,即若干個DML語句組成就是事務)
事物特點:集合中的操作要么一起成功,要么一起失敗
所以才有commit/rollback ,即所有都成功了則提交,否則回滾
2.事務開啟的標志:DML語句開始
3.事務結束的標志:
提交結束:
1.顯示提交 commit
2.隱式提交
執行DDL語句后會自動提交
正常退出:quit命令等 會自動提交
回滾結束:
1.顯示回滾 rollback
2.隱式回滾 異常退出,掉電,宕機等情況會自動回滾
4.事務的4大特性(ACID):原子性,一致性,隔離性,持久性
原子性 (Atomicity):事務中的全部操作在數據庫中是不可分割的,要么全部完成,要么均不執行。
一致性 (Consistency):幾個並行執行的事務,其執行結果必須與按某一順序串行執行的結果相一致。
隔離性 (Isolation):事務的執行不受其他事務的干擾,當數據庫被多個客戶端並發訪問時,隔離它們的操 作,防止出現:臟讀、幻讀、不可重復讀。
持久性 (Durability):對於任意已提交事務,系統必須保證該事務對數據庫的改變不被丟失,即使數據庫出
5.隔離級別
1).對於同時運行的多個事務, 當這些事務訪問數據庫中相同的數據時, 如果沒有采取必要的隔離機制, 就會導致各種並發問題:
(1).臟讀: 對於兩個事物 T1, T2, T1 讀取了已經被 T2 更新但還沒有被提交的字段. 之后, 若 T2 回滾, T1讀取的內容就是臨時且無效的.
(2).不可重復讀: 對於兩個事物 T1, T2, T1 讀取了一個字段, 然后 T2 更新了該字段. 之后, T1再次讀取同一個字段, 值就不同了.
(3).幻讀: 對於兩個事物 T1, T2, T1 從一個表中讀取了一個字段, 然后 T2 在該表中插入了一些新的行. 之后, 如果 T1 再次讀取同一個表, 就會多出幾行.
2). 數據庫事務的隔離性: 數據庫系統必須具有隔離並發運行各個事務的能力, 使它們不會相互影響, 避免各種並發問題.
3). 一個事務與其他事務隔離的程度稱為隔離級別. 數據庫規定了多種事務隔離級別, 不同隔離級別對應不同的干擾程度, 隔離級別越高, 數據一致性就越好, 但並發性越弱
SQL99定義4中隔離級別:
(1).Read Uncommitted 讀未提交數據。
(2).Read Commited 讀已提交數據。 (Oracle默認)
(3).Repeatable Read 可重復讀。 (MySQL默認)
(4).Serializable 序列化、串行化。 (查詢也要等前一個事務結束)
具體說明見圖12:
這4種MySQL都支持
Oracle支持的隔離級別: Read Commited(默認)和 Serializable,以及Oracle自定義的Read Only三種。
(2).Read Commited讀已經提交,意思是如果沒有提交,其他人是讀不到的。
級別越高越好,最高級別也就是(4).Serializable,三種不希望出現的讀 都不會出現,但是這種沒人用,因為是串行的,效率太低
Read Only:由於大多數情況下,在事務操作的過程中,不希望別人也來操作,但是如果將別人的隔離級別設置為Serializable(串行),但是單線程會導致數據庫的性能太差。是應該允許別人來進行read操作的。
6.事務也是關系型數據庫的特點,也就是關系型數據庫一般都支持事務,非關系型的則不支持
7.控制事務
除了commit rollback外還有savepoint(保存點)
保存點,類似還原點,即可以使用回滾的方式恢復到那一時刻(保存的時刻)。如果都不是自己
要回滾的保存點,那就可以用rollback回到最初,也就是直接回滾到上一次commit的時候。
如果沒有回滾,覺得事務結束了,則直接提交即可,代表當前的事務結束了。
具體見圖13:
使用示例:
savepoint aaa;
rollback to savepoint aaa;//回滾到保存點aaa
注意,
如果保存點設置名稱重復,則會刪除之前的那個保存點。
一但commit之后,savepoint將失效。
回退到某個保存點后,則不能在回退到該保存點之后的保存點了。也就是說,不會回退到比當前保存點更新的保存點。
/*******************************************************************************************/
十五、Top-N問題 相關子查詢
1.【第一題】:找到員工表中工資最高的前三名,要求按排名遞增的順序輸出
——涉及Top-N分析問題
Top-N問題: 取前3名
分頁問題:取5-8名
1).Top-N問題分析
(1).一般不在子查詢中使用order by,但是在top-n分析問題中,必須使用order by
(2).rownum 行號(偽列),也就是查詢這個就會顯示行號出來。
(3).rownum 隨着集合的創建就會存在,不會隨着后面修改,也就是無論怎么排序顯示出來,記錄對應的行號是不會變的
(4).由於rownum是按照默認順序生成,所以只能使用<,<=符號,不能使用>,>=符號。
2).Top-N問題答案
SQL> select rownum,empno,ename,sal from ( select * from emp order by sal desc ) e where rownum <=3;
3).分頁問題分析
分頁問題:取5-8名
對新產生的集合(rownum小於8的)中的rownum取別名R,這樣新產生的列R就不是偽列了,就可以使用R>4來過濾了。
4).分頁問題答案
SQL> select *
from (select rownum r, empno, ename, sal
from (select * from emp order by sal desc) e
where rownum <= 8)
where r > 4;
2.【第二題提示】:找到emp表中薪水大於本部門平均薪水的員工
1).兩張表中沒有相同的列,即沒有沖突,所以直接選擇查詢即可:
先得到各個部門的平均薪水
select deptno,avg(sal) from emp group by deptno
把上述結果當成一個集合(表)
SQL>select empno,ename,sal,avgsal
from (select deptno,avg(sal) avgsal from emp group by deptno ) a ,emp e
where a.deptno = e.deptno
and e.sal > a.avgsal
2).相關子查詢寫法:
相關子查詢先執行主查詢,再執行子查詢
原因在於,子查詢的執行需要的東西由主查詢產生,即子查詢依賴於主查詢。
示例:
---求10號部門 大於10號部門平均薪水的員工
SQL>select empno,ename,sal,(select avg(sal) from emp where deptno=10) avgsal
from emp
where deptno=10
and sal > (select avg(sal) from emp where deptno=10)
===> 將10號部門替換為 emp表每條記錄的部門:這個就是相關子查詢
SQL>select empno,ename,sal,(select avg(sal) from emp where deptno=e.deptno) avgsal
from emp e
where sal > (select avg(sal) from emp where deptno= e.deptno)//注意依賴:e.deptno
多表查詢與子查詢都可以解決的時候哪個更好?
一般情況下是多表查詢,多表查詢是以空間換時間,多表查詢一下把數據都加載過來,子查詢和數據庫需多次的交互,
很明顯多表查詢和數據庫的交互更少,一般情況下sql優化的原則都是盡量減少和數據庫的交互
3. 【第三題提示】:統計每年入職的員工個數
分析,
——員工的入職年份是已知條件——1980、1981、1982、1987這4個。
要統計每年入職的人數,一定要知道每個員工的入職日期,可以通過查詢hiredate列來得到
結合查詢結果,以1981年為例,如何統計出81年入職的有多少個人呢?可以從寫C程序的角度入手。
思路:定義一個計數器count=0; 有一個81年的員工,就+1,不是81的就+0;最后查看count的值就可以了。
求和,使用sum函數,內部邏輯:sum(if 是81年 then +1 else +0)
也就是取員工的hiredate列的“年”那一部分,與81比較,進行判斷。
to_char(hiredate, ‘yyyy’) 得到年,與‘1981’進行比較
答案,
select to_char(hiredate,'yyyy') from emp ;
select decode(to_char(hiredate,'yyyy'),'1981',1,0) from emp ;
select sum(decode(to_char(hiredate,'yyyy'),'1981',1,0)) "1981" from emp ;--搞定一列
SQL>select count(*) "Total",
sum(decode(to_char(hiredate, 'yyyy'), '1980', 1, 0)) "1980",
sum(decode(to_char(hiredate, 'yyyy'), '1981', 1, 0)) "1981",
sum(decode(to_char(hiredate, 'yyyy'), '1982', 1, 0)) "1982",
sum(decode(to_char(hiredate, 'yyyy'), '1987', 1, 0)) "1987"
from emp;
注意,
數字不能用作別名,得轉換為字符型,即加上雙引號。
集合的效率一般比較低,即多表組成集合的方式這種效率一般比較低。
具體見《課堂練習—子查詢_多表查詢.docx》
4.統計部門人數小於4的部門情況,顯示部門編號,名稱,部門人數
select d.deptno,d.dname,count(empno)
from emp e,dept d
where e.deptno(+)=d.deptno
group by d.deptno,d.name
having count(empno) < 4
order by 1;
//1表示按照第一列進行排序
復習的時候可以從多表查詢進行復習
/*******************************************************************************************/
十六、表的管理
表的管理包括:創建表,刪除表,修改表,重命名表,表的約束。
1.創建表
表是由行和列組成的。所以創建表的時候就要決定有哪些列。
1).列(里面的)的數據類型,(表示列里面的內容的數據類型)
創建表時, 列所使用的數據類型(oracl):
VARCHAR2 不定長字符,定義了10,存了5個,則只占用5
CHAR 定長字符,定義了CHAR10,則占用的就是10個內存
NUMBER(整數,小數)
ROWID 行地址,是一個偽列(用的時候直接取就行了,就在那里了)。
具體見圖14:
2).創建表的前提條件:表空間 + 權限
Oracle默認存儲是都存為大寫,意思是表名默認存儲在數據庫里的是大寫的,即:
select * from tab;//看到的表名里都是大寫的
也就是說,由於對於字符串大小寫敏感,所以查找表名的時候要大寫,即:
select * from tab where tname = 'EMP'
3).示例:
(1).創建一個表
create table t1(id number(4),tname varchar2(30),hiredate date default sysdate);//t1表名,id是第一列的列名,number
//是第一列列內容的類型為數值型,並指定長度為4,同時defalut的意思是,insert時不想放入數據時,會使用這個默認值
//即第三列插入時不用填:insert into t1(id,tname) values(1,'1234sdf');
SQL> create table t1(id number(4),tname varchar2(30),hiredate date default
sysdate);
表已創建。
SQL>
SQL> desc t1
名稱 是否為空? 類型
----------------------------------------------------- -------- ----------------
--------------------
ID NUMBER(4)
TNAME VARCHAR2(30)
HIREDATE DATE
SQL>
SQL> insert into t1(id,tname) values(1,'yekai');
已創建 1 行。
SQL> select * from t1;
ID TNAME HIREDATE
---------- ------------------------------ --------------
1 yekai 16-2月 -17
(2).創建一個與 t1相同表結構的表
create table t2 as select * from t1 where 1=2;//1=2不成立,集合為空,所以內容不會拷貝過去,只是把表結構(格式)拷貝過去
2.刪除表
//這個前面說過了
當表被刪除:
數據和結構都被刪除
所有正在運行的相關事物被提交
所有相關索引被刪除
DROP TABLE 語句不能回滾,但是可以閃回
1).drop table t2;//這個是刪除到回收站里面了。此時查詢這個表也是查詢不到的。
oracle有回收站機制 ,所以會看到:BIN$A8EsHYSkSiKQfnHrOw4E7g==$0
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BIN$A8EsHYSkSiKQfnHrOw4E7g==$0 TABLE
2).查看回收站
SQL> show recyclebin;//sql plus工具里才看得到結果
ORIGINAL NAME RECYCLEBIN NAME OBJECT TYPE DROP TIME
---------------- ------------------------------ ------------ -------------------
T2 BIN$A8EsHYSkSiKQfnHrOw4E7g==$0 TABLE 2017-02-16:09:45:54
3).閃回表,(flashback 閃回,需要開啟行移動,但是表的閃回不需要)
flashback table t2 to before drop;//oracle 10g才支持的功能,從回收站里恢復(閃回)
注意:並不是所有的用戶都有“回收站”,對於沒有回收站的用戶(管理員)來說,刪除操作是不可逆的。
4).不經過回收站刪除
SQL> drop table t3 purge;//不經過回收站刪除,類似win:shift+delete;
SQL> purge recyclebin;//清空回收站,注意回收站是在服務器中的,如果清除回收站,則有可能把別人的也清除了。
回收站已清空。
3.修改表
增加一列
alter table t1 add email varchar2(30);//t1表增加一列,email是新列的名字,varchar2(30)列內容的類型
修改列屬性(內容的類型)
alter table t1 modify email varchar2(40);//如果表非空,長度往小改,這時如果有數據長度大雨想要修改的長度值,則會修改失敗
修改列名
alter table t1 rename column email to address;
刪除列
alter table t1 drop column address;
4.重命名表
rename t1 to t3;//表是數據庫的對象,rename 是針對對象的。
5.表的約束
1).表的約束的含義:對錄入進來的數據有要求
2).作用:保持數據的一致性。
3).有5種約束(要求):
(1).Primary Key 主鍵約束
要求:唯一+非空
(2).Foreign Key 外鍵約束
要求:主表里有這個東西,子表才可以引用這個東西。比如部門表就是主表,員工表就是子表,員工表依賴於部門表,比如
有個10號部門的員工,則必須先有10號部門。
外鍵還有一個要求:作為子表里的外鍵(比如部門編號)的字段必須是主表里面的主鍵。
外鍵也是關系型數據庫的特點,即兩個表之間有關聯。
(3). Unique 唯一性約束
要求:不能重復。是主鍵要求內的一種要求。
(4).Not Null 非空約束
要求:不能為空。是主鍵要求內的一種要求。
(5).Check 檢查性約束
檢查輸入數據的格式,是否符合要求。
4).constraint 關鍵字,用於創建約束時,給約束起名
5).示例:
create table student(sid number(4) constraint student_PK primary key ,
sname varchar2(30) not null,
sex varchar2(30) check (sex in ('男','女')),
sal number check (sal > 10000),
deptno number(2) references dept(deptno) on delete cascade
);
//constraint 是約束關鍵字表示后面是約束,student_PK是約束名(規范:表名_約束類型),primary key表示這個約束是主鍵
//not null表示約束非空,check是約束中的檢查,Foreign Key(可以省略,由references去指定依賴是哪個表哪一列) 外鍵約束
例如:部門表dept和員工表emp,不應該存在不屬於任何一個部門的員工。用來約束兩張表的關系。
注意:外鍵約束會限制刪除操作,即如果父表的記錄被子表引用的話,父表的記錄默認不能刪除。
默認方式要刪除的解決方法:
1) 先將子表的內容刪除,然后在刪除父表。
2) 將子表外鍵一列設置為NULL值,斷開引用關系,然后刪除父表。
無論哪種方法,都要在兩個表進行操作。所以定義外鍵時,可以通過references指定如下參數:
——ON DELETE CASCADE:當刪除父表時,如發現父表內容被子表引用,(級聯)刪除子表所有引用該內容的記錄。
——ON DELETE SET NULL:當刪除父表時,會先把子表中對應外鍵值置空(SET NULL),再刪除父表。
多數情況下,使用SET NULL方法,防止子表列被刪除,數據出錯。
推薦使用的外鍵方式:默認,先刪除子表,再刪除主表.
6).約束演示:
(1).違反外鍵約束
SQL> insert into student values(1,'yekai','男',100000,50);
insert into student values(1,'yekai','男',100000,50)
*
第 1 行出現錯誤:
ORA-02291: 違反完整約束條件 (SCOTT.SYS_C0011218) - 未找到父項關鍵字
//SYS_C0011218系統自動生成的約束名
(2).違反主鍵約束
SQL> insert into student values(1,'fuhongxue','男',100000,40);
insert into student values(1,'fuhongxue','男',100000,40)
*
第 1 行出現錯誤:
ORA-00001: 違反唯一約束條件 (SCOTT.STUDENT_PK)//STUDENT_PK自己起的約束名,最好約束名都自己起這樣便於自己去查找
SQL> insert into student values(2,'fuhongxue','男',10000,40);
insert into student values(2,'fuhongxue','男',10000,40)
*
第 1 行出現錯誤:
ORA-02290: 違反檢查約束條件 (SCOTT.SYS_C0011216)
SQL> insert into student values(2,'fuhongxue','男',10001,40);
(3).違反性別檢查
SQL> insert into student values(3,'luxiaojia','無',10001,40);
insert into student values(3,'luxiaojia','無',10001,40)
*
第 1 行出現錯誤:
ORA-02290: 違反檢查約束條件 (SCOTT.SYS_C0011215)
--違反非空檢查
SQL> insert into student values(3,null,'無',10001,40);
insert into student values(3,null,'無',10001,40)
*
第 1 行出現錯誤:
ORA-01400: 無法將 NULL 插入 ("SCOTT"."STUDENT"."SNAME")
insert into student values(3,'luxiaojia','男',10001,40);
(4).級聯刪除
SQL> delete from dept where deptno=40;
已刪除 1 行。
SQL> select * from student;
未選定行
推薦使用的外鍵方式:默認,先刪除子表,再刪除主表.
7).查詢表里面的約束:
select constraint_name,constraint_Type,search_condition from user_constraints where table_name = 'STUDENT';
表刪除后,這些約束就沒了。即約束的生命周期是表存在。
/*******************************************************************************************/
十七、其他數據庫對象
數據庫的對象:表、視圖、索引、序列、同義詞
:存儲過程、存儲函數、觸發器、包、包體、數據庫鏈路(datalink)、快照。(12個)
表 基本的數據存儲集合,由行和列組成。
視圖 從表中抽出的邏輯上相關的數據集合。
序列 提供有規律的數值。
索引 提高查詢的效率
同義詞 給對象起別名
數據庫對象中的表相關的前面已經講了,接下來是其他數據庫對象
1.視圖
視圖:從表中抽出的邏輯上相關的數據集合。
所以:1. 視圖基於表。2. 視圖是邏輯概念。3. 視圖本身沒有數據。
使用示例:
1).創建(視圖只能創建、刪除、替換。(不能修改,修改則要加括號里的 替換replace,
同時還有一個作用如果創建的這個視圖已經存在加了這個替換關鍵字就會替換掉舊的(其實也就是相當於修改舊的視圖了),就不會報錯了)
如:剛剛創建的empincomeview,其他語句不變,將create一行改寫成):
create (or replace) view empincomeview as
select e.empno, e.ename, e.sal, e.sal*12 annualsal, e.sal*12+nvl(comm, 0) income, d.dname
rom emp e, dept d
where e.deptno = d.deptno;
2).使用:
select * from empincomeview;
沒有權限,需要先登錄 dba
C:\Users\Administrator>sqlplus sys/sys@orcl100 as sysdba
授權給scott
grant create view to scott;
視圖本身沒有數據,邏輯概念,數據依賴於表.即數據是從表里面來的。
3).作用:簡化復雜查詢,隔離數據訪問(從視圖中獲取,也就是查詢時不知道具體的查詢語句,只有創建者才知道)。
相當於作了替換,用視圖替換復雜的查詢語句,
即執行查詢視圖,相當於執行查詢視圖中的語句
總結一句話:不通過視圖做insert、update、delete操作。因為視圖提供的目的就是為了簡化查詢。
4).刪除視圖:SQL> drop view testview; //不加“purge”關鍵字(因為視圖本身都沒有數據,即不會經過回收站)。
2.序列
序列 提供有規律的數值。
1).可以理解成數組:默認,從[1]開始,長度[20] [1, 2, 3, 4, 5, 6, …, 20] 在內存中。
20個是在內存中,如果用完了則會再加載20個,也就是緩沖長度是20 *
由於序列是被保存在內存(即實例中)中,訪問內存的速率要高於訪問硬盤的速率。所以序列可以提高效率
2).create sequence myseq;//myseq為序列的名字
3).序列的2個值:nextval,currval ,引用時語法,序列名.xxxval ,:
獲取序列會使用到這兩個值,注意,可以理解為序列里面蘊含着一個指針,
使用nextval獲取序列值的時候,獲取的是序列中指針指向的下一個元素,同時獲取后指針會加一,即
此時再使用currval獲取的就是剛才nextval獲取的值,因為指針移動了,
同時 下次再使用nextval獲取序列值的時候,獲取的是下下一個元素。
當表剛創建好時,此時指針指向的是第一個元素的前一個位置,即使用currval獲取不到任何元素,
要使用 nextval才可以開始使用
SQL> create sequence myseq;
SQL> select myseq.currval from dual;//獲取當前序列元素
select myseq.currval from dual
*
第 1 行出現錯誤:
ORA-08002: 序列 MYSEQ.CURRVAL 尚未在此會話中定義
SQL> select myseq.nextval from dual;//獲取下一個序列元素
NEXTVAL
----------
1
drop sequence myseq;// 刪除序列
4).序列的作用:一般給主鍵使用字段使用
insert into dept values(myseq.nextval,myseq.nextval||'name',myseq.nextval||'loc');//序列給主鍵字段使用
循環執行這個語句,即可實現插入不同的數據,也就是可以插入唯一的主鍵
注意上述語句是一條語句,所以nextval是一樣的值,也就是引用nextval是在一條語句里面跑的時候值是一樣的。
5).查詢哪一列是主鍵的其他方式:
ed dept;//dept是表名,執行這個命令后再選擇keys時 則會有對應列信息的展示。
具體見圖15:
6).什么情況下主鍵不連續?
一個序列多個表使用
數據刪除
執行rollback ,序列不進行回滾(原因是序列在實例中,所以回滾不了),
所以記錄恢復到以前了,即之前獲取到的序列值沒了,同時序列沒有恢復,也就是再次獲取的序列值
是回滾刪除掉的記錄中的序列的下一個,那么這個序列值是和刪除掉的序列值連續的,因為是刪除掉的,所以此時
就不連續了。
3.索引
1).索引需要占用空間,需要權限.
作用,提高查詢效率,,提高查詢效率的原因就是有序
由於原有的數據是不能進行排序的(不能修改數據),所以需要創建一個有序的,這個創建的有序對象就是索引
2).關於索引的創建:
對索引值進行排序,然后再把索引值和行地址關聯起來,這樣(即創建索引后)就可以,
通過索引確定行地址(rowid,代表當前記錄在內存中地址),通過行地址得到數據.
3).創建索引:
--create index indexname on tablename(columnname1,columnname2,...);
create index myinde on emp(ename);//create index 創建索引,myinde 索引名,on emp(ename) 使用emp(ename)作為索引
索引一旦創建就形成了索引表,由oracle自行維護. 默認索引是btree索引,還有一種索引是位圖索引
4).刪除索引:
drop index indexname;
5).使用索引:
在查詢條件里有索引的條件,即要使用 作為索引值的列 來進行查詢,
如上訴例子中就要使用ename來進行查詢才會用好索引。
create index indexname on tablename(columnname1,columnname2,...);
//創建了多個列作為索引,稱為聯合索引,聯合索引的使用是多個索引值都要作為查詢條件,即
columnname1,columnname2,... 都要作為查詢條件。
6).索引是為了提高查詢效率的,所以:
以下情況可以創建索引:
列中數據值分布范圍很廣
列經常在 WHERE 子句或連接條件中出現
表經常被訪問而且數據量很大 ,訪問的數據大概占數據總量的2%到4%
下列情況不要創建索引(沒有意義,浪費空間):
表很小
列不經常作為連接條件(查詢條件)或出現在WHERE子句中
查詢的數據大於 2%到4% //這個指標只是建議值
表經常更新
4.同義詞
給對象起別名
1).同義詞的意思
實際生產環境(工作環境,即數據庫的數據都是真實的)中,一般開發者之類的用戶都是只有查詢權限,
如果想要修改則必須通過公司開發的修改工具去修改,修改工具會記錄具體誰去修改了,以及修改的內容都會記錄。
並且當前用戶並不是所有的數據都可以查詢的,如果想查詢其他用戶的數據,要select * from 用戶名.表名
但是有時候用戶名過長,所以一般在查詢用戶建立同義詞。
同義詞的意思是,在當前用戶下建立一個和其他用戶相同名字的對象,這個對象名叫同義詞
比如在當前用戶下創建一個也叫emp的(可以認為是表,但是實際上是同義詞),然后查詢的時候就可以直接 select * from emp;
相當於select * from 用戶名.表名,即相當於作了一個替換。
所以同義詞的作用也是:簡化復雜查詢,隔離數據訪問
2).創建示例:scott用戶訪問hr用戶中的表,
(scott用戶看不到 數據庫中hr用戶能看到的表。默認情況下創建一個用戶並在該用戶下創建的表其他用戶是看不到的,
即使用 select * from 其他用戶名.表名 也無法訪問,
想看到的話,需要其他用戶或者系統管理員去授予權限:
grant select,update,insert on employees to 其他用戶;
//給予其他用戶select,update,insert當前用戶下employees表的權限,此時就可以通過 select * from 其他用戶名.表名 查看了)
(1).在hr用戶下給scott用戶賦權:grant select on employees to scott;
能查看其他用戶的表的時候,就可以建立同義詞了:
(2).scott用戶(查詢用戶)下:
此時可以查看hr用戶employees,因為已經賦權
SQL> select * from hr.employees;
創建同義詞
SQL> create synonym employees for hr.employees;
//create synonym 創建同義詞 ,employees 同義詞名,for hr.employees 等同於hr用戶下的employees表
(3).創建權限不足,需要dba用戶
給scott創建同義詞的權限:
grant create synonym to scott;
3).刪除同義詞
drop synonym employees;
4).新創建一個用戶,再創建一個查詢用戶,如何在查詢用戶批量創建新創建用戶的同義詞?
思路: tab 字典 (tab 字典下可以看到當前用戶下所有的表)===> 批量生成 create 語句.
//tab 字典下可以看到當前用戶下所有的表,然后考慮如何用這些表批量生成 create 語句:
reate synonym employees for hr.employees;