Oracle數據庫 —— DML完結

 

時間:2016-8-18 01:17java

 

----------------------------------------------------------------------------
停下休息的時候不要忘記,比你強的人還在奔跑
----------------------------------------------------------------------------
 

 
 
第一章:數據庫系統概述

1、數據庫的產生動機
    1.1 全部事物的產生都必定會有它的客觀規律,對於計算機而言,最先是爲了解決計算問題,而數據庫的產生主要也是爲了解決數據的有效管理。

    1.2 數據庫,顧名思義,存儲的確定都是數據,而數據庫的產生是爲了解決商業管理中的數據應運而生的。

    1.3 數據庫能夠解決數據的統一管理問題。

2、數據、數據庫、數據庫管理系統、數據庫系統
    2.1 數據:描述事物的符號記錄稱爲數據
        數據是數據庫中存儲的基本對象,除了基本的數字以外,像圖書的名稱、價格、做者等均可以稱爲數據。
    2.2 數據庫
        是存放數據的倉庫,全部的數據在計算機存儲設備上保存,並且全部保存的數據會按照必定的格式進行保存。
        數據庫是長期存儲在計算機內、有組織的、可共享的大容量數據的集合,數據庫中的數據按必定的數據模型組織、描述和存儲,具備較小的冗餘度、較高的數據獨立性和易擴展性,並能夠爲各類用戶共享,因此數據庫具備永久存儲、有組織和可共享的三個基本特色。
 
    2.3 數據庫管理系統(DataBase Management System DBMS)
        科學的組織和存儲數據、能夠高效的獲取和維護數據。
        數據庫管理系統和操做系統同樣是計算機的基礎軟件,也是一個大型複雜的軟件系統,主要功能包括如下幾個方面:數據操做功能,數據庫的事務管理和運行管理,數據定義功能,數據組織、存儲和管理,數據庫的創建和維護功能。
        數據庫的技術主要是由於要進行數據管理而產生的,利用數據管理能夠進行數據的分類、組織、編碼、存儲、數據的檢索查詢、維護的等。
        數據管理技術主要通過三個結點:人工管理,文件系統管理,數據庫系統管理。
 
3、數據結構化
    3.1 數據庫系統能夠實現結構化的數據保存,比起文件系統而言,數據庫中所保存的數據會按照一個統一的標準形式操做,而全部的數據能夠按照不一樣的性質保存在不一樣的數據表中。
    3.2 除了數據結構化的特徵以外,還包含了數據的共享性,數據的冗餘度低,並且方便擴充。
    3.3 在進行數據庫的管理過程當中,數據的獨立性很高,全部的數據都是經過DBMS進行操做的,那麼全部的數據庫確定是要經過程序進行訪問的,因此這個時候程序必須通過DBMS以後才能夠訪問數據庫中的數據。
    3.4 也就是說DBMS是統一管理、控制數據、安全性保護、完整性檢查、併發操做、數據恢復的系統。
    3.5 因此數據庫中的數據必定是長期存儲的,並且是有組織存儲的,爲不一樣的用戶提供數據的操做,也保證了數據的完整性和安全性。
 
4、數據模型(Data Model)
    通常來說,數據模型是嚴格定義的一組概念的集合,主要是用於描述數據或信息的標記,所以數據模型一般由數據結構、數據操做和完整性約束三部分組成。
    4.1 兩類數據模型
        是對客觀世界中的某些事物的特徵數據抽象和模擬,是嚴格定義的一組概念,即數據模型是用來描述、組織數據、而且對數據進行操做的,數據模型是整個數據庫系統的核心。
    4.2 物理模型
        對物理數據模型而言,是在開發中使用比較多的一種,包括作的數據庫的設計都是經過此模型進行的。
    4.3 概念模型
    4.4 在進行模型創建的時候應該知足三個方面要求:
        比較真實的模擬現實世界。
        容易被人所理解。
        便於計算機的實現
    4.5 根據模型的不一樣應用,能夠將這些模型分爲兩類:
        概念模型:信息模型,主要用於數據庫的設計
        邏輯模型和物理模型:包括層次模型、網狀模型、關係模型、面向對象模型、對象關係模型。主要是按照計算機系統的觀點對數據進行建模。
    4.6第二類的模型是對數據底層的最基本的抽象,描述的是一些存儲相關的內容。抽象流程:現實世界→抽象出概念模型→邏輯模型的實現。
 
5、概念模型
    在概念模型中,主要涉及的基本概念有:實體(Entity)、屬性(Attribute)、碼(key)、域(Domain)、實體型(Entity Type)、實體集(Entity Set)、聯繫(Relationship),下面分別來結實這些基本概念。
    實體(Entity)
        客觀存在並可互相區別的事物稱爲實體,實體能夠是具體的人、事、物,也能夠是抽象的概念或聯繫。
    屬性(Attribute)
        實體所具備的某一特性稱爲屬性,一個實體能夠有若干個屬性來描述。
    碼(key)
        碼也能夠稱爲關鍵字,它能夠惟一標識一個實體。
    域(Domain)
        屬性的取值範圍稱爲屬性的域(Domain)。
    實體型(Entity Type)
具備相同屬性的實體必然具備共同的特徵和性質,用實體名以及其屬性名集合來抽
象和刻畫同類實體。
    實體集(Entity Set)
        同一類型的實體集合稱爲實體集。
    聯繫(Relationship)
 
    5.1 兩個實體之間的聯繫
        兩個實體之間的聯繫能夠分爲三種:一對一,一對多,多對多。
    5.2 單個實體內的聯繫(自反聯繫)
        同一個實體集內的各個實體之間也能夠存在一對一,一對多,多對多的聯繫,例如:公司之中會存在許多的僱員,每個僱員都會有一個領導,而公司的最大領導沒有領導,這個時候一個僱員的實體被另一個僱員直接領導,所以這也是一種一對多的聯繫。
        對於自反聯繫,在多表查詢中就會看見其使用。
 
6、數據模型
    6.1 數據結構
        指的是對象和對象之間的聯繫,能夠描述出一個客觀事物的類型、內容相關的數據項,包括彼此之間的練習。
    6.2 數據操做
        針對於單挑記錄或者是多條記錄進行操做,主要就是兩類:查詢、更新,這些所有由SQL定義了。
    6.3 完整性約束
        保證表中的數據是有意義的。
        爲了保證數據庫中所保存的數據是由正確的意義的,在數據庫中專門爲全部操做的數據提供了一組完整性的規則,全部的數據必須符合這些既定的規則才能夠操做。
    6.4 重要的數據模型
        在如今的數據庫系統之中有兩種很是重要且比較優秀的數據模型:
        關係數據模型:
            包括對象關係模型的擴展,在現行的全部商業數據庫管理系統中都有出現。
        半結構化數據模型:
            包括XML,它是大多數關係DBMS的一個附加特徵。
    6.5 關係數據模型
        關係數據模型是一種基於表的數據模型。
    6.6 半結構化模型
        半結構化數據模型相似於樹或者是圖,而非表或數據。
        就像XML中的DOM樹。
    6.7 層次模型
        層次模型是數據庫系統中最先出現的數據模型,層次數據庫採用層次模型做爲數據的組織方式。層次數據庫的典型表明是IBM公司的IMS(Information Management System)數據庫管理系統,這是1968年IBM公司推出的第一個大型的商用數據庫管理系統,曾獲得普遍的應用,在數據庫中定義知足下面兩個條件的基本層次聯繫的集合爲層次模型:
        有且只有一個節點沒有雙親結點,這個節點稱爲根節點。
        根節點之外的其餘節點有且只有一個雙親結點。
    6.8 網狀模型
        在現實世界中事物之間的聯繫更多的是非層次的關係,因此此時就沒法使用層次模型表示非樹形結構,而經過網狀模型則能夠克服這一弊病。
        網狀數據庫系統採用網狀模型做爲數據的組織方式。
        與層次模型同樣,網狀模型中每一個節點表示一個記錄類型(實體),每一個實體可包含若干個字段(實體的屬性),節點間的連線表示實體之間一對多的父子聯繫。
    6.9 關係模型
        關係模型是目前最重要的一種數據模型,關係數據庫系統採用關係模型做爲數據的組織方式,1970年美國IBM公司San Jose研究室的研究員E.F.Codd首次提出了數據庫系統的關係模型,開創了數據庫關係方法和關係數據理論的研究,爲數據庫技術奠基了理論基礎,因爲E.F.Codd的傑出工做,他於1981年得到ACM圖靈獎。
        關係模型爲人們提供了單一的一種描述數據的方法:一個稱之爲關係(Relation)的二維表,關係表中的每一行對應一個實體,每一列對應這個實體的一個特徵。
    6.10 關係模型術語
        屬性:關係的列命名爲屬性(Attribute)
        模式:關係名和其樹形集合的組合成爲這個關係的模型(Schema)
        元組:關係中除含有屬性名所在的行之外的其餘行稱爲元組(Tuple)
            元組能夠理解爲一行數據。
        域:關係模型要求元組的每一個份量具備原子性(不可再分)
 
7、SQL概述
    7.1 什麼是SQL
        SQL(Structured Query Language),即結構化查詢語言,是關係數據庫的標準語言,SQL是一個通用的、功能強大的關係數據庫語言,當前,幾乎全部的關係型數據庫管理系統軟件都支持SQL,許多軟件廠商還對SQL基本命令進行了不一樣程度的擴充。
    7.2 SQL語言有如下幾個部分
        數據操做語言(Data Manipulation Language)
            用於檢索或者修改數據。
        數據定義語言(Data Definition Language)
            用於定義數據的結構,建立、修改或者刪除數據庫對象。
        數據控制語言(Data Control Language)
            用於定義數據庫用戶的權限。
        完整性(Integrity)
            SQL DDL包括定義完整性約束的命令,保存在數據庫中的數據更新時必須知足所定義的完整性要求,不然沒法更新。
        視圖定義(View Definition)
            SQL DDL包括定義視圖的命令。
        嵌入式SQL和動態SQL(Embedded SQL and Dynamic SQL)
            主要定義如何將SQL嵌入到通用編程語言,如C C++ Java中。
        事務控制(Transaction Control)
            定義了包含事務開始和結束的相關命令。
    7.3 世界上最先支持SQL標準的公司是Oracle,而SQL標準是由IBM公司提出的概念,如今幾乎全部數據庫都支持了SQL標準,同時不一樣的數據庫廠商也對SQL作了擴充,如今最新的SQL標準是SQL2008.
        只要是數據庫,必定會支持SQL標準,這樣的作法是極大的方便了程序開發人員。
        SQL是一個標準,但是必需要說明的是,在數據庫的發展之中,一直存在一種稱爲NOSQL的技術,這個概念的提出主要是爲了抵制SQL標準。並且隨着大數據的發展,對於NOSQL技術又從新獲得了審視,因此對於NOSQL再也不翻譯爲不適用SQL,而是變爲了not only SQL,其中的表明數據庫是MongoDB數據庫(Node.js開發)。
 
8、總結
    8.1 數據庫技術的出現是爲了解決數據維護問題的,使用數據庫進行數據的管理要比使用手工方式的數據管理更加方便。
    8.2 數據模型是對客觀世界中的某些事物的特徵數據抽象和模擬,分爲概念模型和物理數據模型兩個方面。
    8.3 實體之間的關係有三種:一對一,一對多,多對多。
    8.4 數據模型如今主要使用關係模型和半結構化數據行,而層次模型,網狀模型等已經再也不使用,。
    8.5 數據庫主要使用SQL命令進行操做,SQL命令分爲三類:DML,DDL,DCL。
 
 
——Oracle簡介

1、Oracle簡介
    1.1 Oracle公司是全球最大的信息管理軟件及服務供應商,成立於1977年,主要的業務是推進電子商務平臺的搭建,Oracle公司有本身的服務器、數據庫、開發工具、編程語言,在行業軟件商還有企業資源計劃(ERP)軟件、客戶關係管理(CRM)軟件,人力資源管理軟件()HCM)等大型管理系統,因此Oracle是一家綜合性的國際大公司,也是最有實力與微軟公司在技術上一較高低的公司之一。
    1.2 Oracle公司的創辦決定於四位傳奇人物Ed Oates、Bruce scott、Bob Miner、LarryEllison。
    1.3 Oracle是商業應用的軟件供應商,例如:銀行,電話,購物,那麼這樣的程序一共會由四個部分組成,操做系統,數據庫,中間層,編程語言。除了提供商業開發的支持以外,還提供了一些商業軟件。
    1.4 Larry Ellison是Oracle公司的締造者,也是Oracle公司發展的領導者。
    1.5 Larry Ellison最先提出了電子商務的概念,而且讓Oracle公司積極致力於發展電子商務的解決方案,而且在1995年以後迅速將Oracle公司的重點發展到了網絡上(這一點能夠隨着Oracle8i(Internet)版本的退出而更加的明顯),而且於2009年以74億美金受夠了SUN公司,今後以後表着這Oracle公司將成爲業界惟一一家提供綜合系統的廠商,將擁有本身的編程語言(Java)、數據庫(Oracle、MySql)、中間件(受夠了BEA的WebLogic)、操做系統(Solaris、Unix)、服務器,這樣一來甲骨文公司在整個行業上的地位更加穩固,使Java語言的發展前景愈來愈好。
2、瞭解Oracle的發展歷史及主要版本
    2.1 Oracle數據庫常見版本
    Oracle八、Oracle8i
        其中8i表示Oracle正式向Internet上開始發展,其中i表示的就是Internet。
            Oracle9i
                Oracle8i是一個過渡版本的數據庫,而Oracle9i是一個更加完善的數據庫版本。
            Oracle10g
                是業界第一個完整的、智能化的新一代Internet基礎架構,爲用戶帶來了更好的性能,其中g表示的是網格,即:這種數據庫採用了網格計算的方式進行操做,性能更高。
            Oracle11g
                Oracle10g的穩定版本,也是如今使用比較普遍的新版本,
            Oracle12c
                Oracle2013年最新版本的數據庫版本,其中c帶包的是雲計算,同時Oracle12c中也支持了大數據的處理能力。
3、能夠進行Oracle數據庫的安裝
    3.1 在Oracle全部服務中,有兩個服務時最重要的:
        數據庫監聽服務:Oracle OraDB11Home_1TNSListener
            若是說如今要經過遠程客戶端鏈接數據庫,或者直接利用程序進行數據庫的鏈接,那麼此服務必須開啓。
        數據庫的實例服務:OracleServiceORCL
            Oracle自己是一個平臺,平臺裏面能夠有多個數據庫,那麼每個數據庫都會存在這樣一種服務名稱:OracleServiceSID
4、能夠使用SQLPlus和SQLDeveloper進行Oracle數據庫的操做
    4.1 sqlplus
        對於Oracle的前臺工具而言,我的有一些見解:
            Oracle9i:OEM(企業管理器)、sqlplusw.exe sqlplus.exe
            Oracle10g:sqlplusw.exe sqlplus.exe EM
            Oracle11g:sqlplus.exe SQL Developer
            Oracle12c:sqlplus.exe SQL Developer
        能夠發現,對於sqlplus而言,是從最先一直延續到今天的工具,因此之後的開發中對於這個工具的一些命令要清楚。
    4.2 sqlplus簡介
        sqlplus是Oracle數據庫提供的一個專門用於數據庫管理的交互式工具,使用sqlplus能夠管理Oracle數據庫的全部任務,sqlplus經過命令的方式對數據庫進行管理,也能夠經過執行SQL語句進行操做。
    4.3 可是若是想要講解sqlplus命令,那麼還須要先解決一個新的問題,那就是數據問題。在Oracle12c中因爲存在了CDB和PDB概念,因此所謂的測試數據默認是找不到的,那麼就須要進行數據恢復。
        在「\app\oracleuser\product\12.1.0\dbhome_1\RDBMS\ADMIN」目錄中有一個scott.sql腳本,可是這個腳本須要修改,並且想要會修改須要學習完本書的第二部分。
        若是要想進行數據的配置。那麼執行的順序以下:
            打開sqlplus(輸入sqlplus nolog)-->執行sql腳本
    4.4 sqlplus經常使用命令:
        設置每行顯示的記錄長度:
            set linesize 300
        設置每頁顯示的記錄數
            set pagesize 30
        使用ed命令調用本機記事本程序
            ed sql腳本名
        經過「@sql腳本名」來執行腳本文件。
            用戶鏈接數據庫
            conn 用戶名/密碼 [as sysdba]
            若是使用sys用戶登陸,須要加上as sysdba參數。
            鏈接成功後,若是想知道當前用戶是誰,能夠使用:show user命令查看。
            若是想經過sys繼續查詢scott用戶中的emp表,那麼確定沒法查詢,這時候必須在表前加上用戶名,即:select * from scott.emp,在數據庫原理之中,用戶名有時候能夠被簡單的稱爲模式名稱。因此全部的表都是具有模式名稱的,即模:模式名.對象名。
        取得當前用戶的所有對象
            select * from tab;
        查看錶結構:
            desc 表名;
        使用本機的操做系統命令:
            host 命令…;
    4.5 關於原始用戶的問題(瞭解)
        一直在強調一點:在Oracle12c中,雖然選擇了要進行樣本數據的建立,不過遺憾的是,發現根本沒有scott和sh用戶。
        scott和h用戶真實存在,如今也在數據庫中,但是默認狀況下用戶全部操做的數據都保存在CDB中,並且在這裏面的用戶名必須以C##開頭,例如C##scitt。
        第一步:須要使用sys登陸
            conn sys/change_on_install as sysdba;
        第二部:查看如今的容器名稱
            show con_name;
            能夠清楚的發現,如今反悔的是一個「CDB$ROOT」,表示的是一個CDB容器。
        第三步:改變容器爲PDB
            alter session set container=pdbORCL;
        第四步:若是未打開數據庫,則先打開
            alter database pdbORCL open;
            若是如今不是在PDB容器之中,那麼還須要在命令上增長一個pluggable參數,即:alter pluggable database pdmORCL open;
        第五步:查看用戶
            select user name from dba_users where username=’scott’ or username=’sh’;
            此時就已經能夠荊楚的發現,scott和sh兩個用於出現了。
        第六步:切換回CDB
            若是想要切換回CDB,只須要從新登陸,或者直接輸入切換命令。
                alter session set container=cdb$root;
    4.6 關於SQL Developer配置
        在Oracle學習中,SQL Developer應該算是如今的重點,這個工具是在Oracle11g以後開始爲用戶提供的。
        這個工具依賴Java環境,因此須要進行Java相關命令的配置,路徑:
            \app\oracleuser\product\12.1.0\dbhome_1\jdk\bin
        若是想要使用此工具,那麼就必須創建新的鏈接,爲了後續學習方便,建議創建兩個鏈接:
            普通用戶:scott/tiger
            管理員:sys/change_on_install
5、掌握scott用戶提供的四張數據表的做用及表結構。
 
6、小結
    6.1 Oracle數據庫是大型關係型數據庫
    6.2 Oracle數據庫中的四個主要用戶:
        超級管理員:sys/change_on_install
        普通管理員:system/manager
        普通用戶:scott/tiger
        海量數據用戶:sh/sh
    6.3 Oracle安裝後兩個最重要的服務時監聽和數據庫實例服務。
    6.4 scott用戶的四張數據表











——SQL語法
 
    對於Oracle數據庫而言,分爲兩個部分:
        Oracle開發部分,直接與程序人員有關,基本SQL使用+PL/SQL編程。
        Oracle管理部分,數據的配置運行維護有關。
    基本要求:
        掌握SQL的基本語法;
        瞭解投影在SQL中的使用;
        能夠使用SQL語句完成簡單的查詢功能。
1、簡單查詢
    在以前咱們執行過查詢一張數據表中的所有數據操做。
        select * from emp;
    這個時候能夠發現會返回emp表中的所有數據記錄,因此這種返回所有數據的查詢,就能夠理解爲簡單查詢。
    在取得所有數據以後,能夠發現某些列上會顯示null的信息,那麼所謂null表示的是沒有內容,不是0或者空字符串。
 
    1.1 簡單查詢語句語法
        select [distinct] * | 列名[as][列別名], 列名[as][列別名]…. from 表名[表別名]
        在簡單查詢中,主要由兩個子句完成:
            select子句
            from子句
 
    1.2 select子句
        *    表示查詢全部數據列。
        列名     表示顯示指定的列,列也能夠設置別名,在輸出的二維表中會打印出來。
        distinct 去除重複數據。
 
    1.3 from子句
        定義要使用的數據表,能夠理解爲數據來源。
 
    1.4 範例
        如今要求查詢出公司的僱員僱用信息,因此但願經過數據庫能夠查到每一個僱員的編號、姓名、基本工資三個信息進行瀏覽:
            select empno,ename,sal from emp;
        須要注意的是,程序的執行順序問題,如今有兩個子句,那麼之後還會牽扯到更多的子句,那麼這兩個子句的執行順序是:
            第一步執行from子句,表示肯定數據來源。
            第二步執行select子句,肯定要顯示的數據列。
 
2、其餘查詢操做
    1.1 範例
        要求查詢公司中全部僱員的職位信息。
            select job from emp;
        職位的字段是job字段,那麼如今只要求查詢職位,因此直接在select子句以後設置job字段便可。
        問題:
            能夠發現,在返回的數據之中存在了重複的內容,由於很明顯,在實際的公司中,一個職位確定會有多我的員的,因此如今若是查詢的是所有的職位,確定會存在重複,若是想要去掉重複的數據記錄,那麼能夠利用distinct來完成。
            雖然使用distinct能夠去除所有的重複內容,可是隻侷限於全部列的內容所有相同的狀況,也就是說,若是有一列與其餘列不一樣,則不會被去除。
 
    1.2 範例
        在select子句中,也能夠進行四則運算。
        要求經過數據庫查詢出全部僱員的編號、僱員姓名和年基本工資,日基本工資。
            select empno,ename,sal*12,sal/30 from emp;
        如今須要知道年薪 = 月薪 * 12,同時還須要知道日薪,日薪 = 月薪 / 30。
        對於小數部分的處理,留給單行函數完成。
 
    1.3 範例
        如今公司每一個僱員在年末的時候能夠領取5000元的年終獎金,要求查詢僱員編號、僱員姓名、和增加後的年基本工資(不包括佣金)。
        分析:這個時候的年薪包含了5000元的年終獎金。
            select empno,ename,sal*12+5000 from emp;
 
    1.4 範例
        公司每月爲僱員增長200元的補助,此時,要求能夠查詢出每一個僱員的編號、姓名、基本年工資。
            select empno,ename,(sal+200)*12+5000 from emp;
        分析:
            如今每月的實際收入是sal+200,可是因爲要與12進行乘法運算,因此須要使用小括號來改變優先級。
 
    1.5 別名
        可是如今出現了新的問題:
        在字段名中出現了(sal+200)*12+5000,這是什麼含義?
            既然如今的表述不夠明確,那麼最好爲其定義一個別名:年薪。
                select empno,ename,(sal+200)*12+5000 as 年薪 from emp;
            在進行別名設置的時候,能夠經過as關鍵字來進行操做,對於最終的結果沒有任何影響,可是,若是要進行別名的設置,在程序中確定是沒用的,那麼在顯示中用處也不大,並且要記住,不要使用中文。
    1.6 字符串常量
        此時有一個新的問題出現了,那麼給出的年薪是什麼樣的年薪呢?人民幣仍是美圓?因此如今但願能夠輸出一個標識,下面就直接輸出一我的民幣標識:
            select empno,ename,(sal+200)*12+5000 as 年薪,'¥' as 貨幣 from emp;
        會發如今最後一列會輸出一個’¥’標識符,而這個貨幣的標識符實際上就是一個字符串常量,常量都使用直接輸出的方式進行定義。
 
    1.5 使用‘||’進行鏈接顯示
        在進行簡單查詢的時候,能夠直接利用||進行字符串或者列數據的鏈接操做。
            select '編號是:' || empno || '的僱員姓名是:' || ename || ',基本工資是:' || sal from emp;

    1.6 小結
        簡單查詢是將一張表中的所有或部分列進行顯示的操做。
        簡單查詢中經過‘*’表示查詢所有的內容,也能夠指定具體的列名稱,顯示具體列的內容。
        在SQL中能夠使用+ - * / 進行四則運算,可是要注意運算符的優先級。
        能夠爲一個顯示的列進行別名的設置,這樣之後顯示時會將相應的列名稱替換成別名來顯示。
        經過||能夠進行數據的鏈接,在查詢語句中出現的字符串,必須使用單引號括起來。
 
3、限定查詢
    簡單查詢的主要特徵就是將一張數據表中的數據所有顯示,然後能夠利用select子句來控制所要輸出的列。
    要求:
        掌握限定查詢的語法格式
        掌握各類常見的限定查詢關係符

一、限定查詢 
    1.1 爲何須要限定查詢?
        若是一張表中有100萬條數據,一旦執行了select * from 表 語句以後,則將在屏幕中顯示錶中所有數據行的記錄,這樣既不方便瀏覽,也有可能形成死機的問題,因此此時就必須對查詢的結果進行篩選,只選出對本身有用的數據便可,那麼就能夠經過where指定查詢的篩選條件。
 
    1.2 限定查詢語法
        select [distinct] * | 列名 [as][列別名], 列名 [as][列別名],…
        from 表名[表別名]
        [where 條件(s)];
        在這個語法中,就是比以前的語法多了一個where子句,在where子句中能夠設置一系列的過濾條件,而這些條件能夠設置多個,不一樣條件之間能夠用邏輯運算符進行鏈接。
    1.3 邏輯運算符
        在編寫where子句判斷條件時,能夠同時指定多個判斷條件的鏈接,而鏈接主要經過邏輯運算符實現,邏輯運算符一共有如下三種:
            與(and)
                鏈接多個條件,多個條件同時知足時才返回true,有一個條件不知足就是false。
            或(or)
                鏈接多個條件,多個條件之中只要有一個返回true,結果就是true,若是多個條件返回的都是false,則結果纔是false。
            非(not)
                取反操做,能夠將true變爲false,將false變爲true。
    1.4 範例
        統計出基本工資高於1500的所有僱員的信息
            select * from sal > 1500;
        咱們能夠發現並非全部的數據都顯示了,只顯示了部分符合條件的數據。
            如今對於SQL語法而言,就具有了三個子句:
                第一步:執行from子句,控制數據的來源。
                第二步:執行where子句,使用限定符來對數據進行過濾。
                第三步:執行select子句,肯定要顯示的數據列。
            爲何where子句不能使用select子句定義的別名?
                由於執行順序問題,where是在select以前執行。
 
    1.5 對數據進行限定查詢
        在以前所使用的>是一個關係運算符,在標準SQL之中定義了許多的運算符。
        關係運算符:進行大小或相等的比較,其中不等號有兩種:!=、<>
        判斷null:is null、is not null
        邏輯運算符:and表示多個條件必須同時知足,or表示只須要有一個條件知足便可,not表示取反。
        範圍查詢:between 最小值 and 最大值,在一個指定範圍內進行查找,查找結果爲:最小值<=內容<=最大值。
        範圍查詢:in,經過in能夠指定一個查詢的範圍。
        模糊查詢:like,能夠對指定的字段進行模糊查詢。

二、關係運算符
    2.1 關係運算符——範例
        如今要求查詢出全部基本工資小於等於2000的所有僱員信息。
            select * from emp where sal <= 2000;
 
    2.2 關係運算符——範例
        根據以前的查詢結果發現,SMITH的工資最低,因此如今但願能夠取得SMITH的詳細資料:
        select * from emp where ename = ‘SMITH’;
 
    2.3 關係運算符——範例
        查詢出全部業務員(CLERK)的僱員信息。
            select * from emp where job = ‘CLERK’;
        注意:在使用關係運算符判斷字符數據的時候必定要注意大小寫的問題,由於Oracle是區分大小寫的。

     2.4 關係運算符——範例
        取得了全部辦事員的資料以後,爲了和其餘職位的僱員進行比較,現決定再查詢全部不是辦事員的僱員信息。
            select * from emp where job != 'CLERK';
 
    2.5 關係運算符——範例
        查詢出工資範圍在1500~3000(包含1500和3000)的所有僱員信息。
            select * from emp where sal >=1500 and sal <= 3000;
            select * from emp where sal between 1500 and 3000;
 
    2.6 關係運算符——範例
        查詢職位是銷售人員,而且基本工資高於1200的全部僱員信息。
            select * from emp where job = ‘SALESMAN’ and sal > 1200;
        如今也一樣是兩個條件,一個條件是字符串職位的判斷,一個是數值工資的判斷。
 
    2.7 關係運算符——範例
        要求查詢出10部門中的經理或者是20部門中的業務員的信息。
            select * from emp where (deptno = 10 and job = 'MANAGER') or (deptno = 20 and job = 'CLERK');
        對於這樣的操做其實是存在兩組條件的:
            條件一:10部門經理
            條件二:20部門的業務員
        這兩個條件有一個知足便可,因此這兩組條件中間應該使用or關鍵字進行鏈接。
 
    2.8 關係運算符——範例
        查詢不是業務員切基本工資不大於2000的所有僱員信息。
        基本實現:select * from emp where job != 'CLERK' and sal > 2000;
        使用not對條件求反:select * from emp where not (job = 'CLERK' or sal <= 2000);
        分析:
            對職位以及工資進行判斷,這兩個判斷條件須要同時知足。

三、範圍查詢
    語法:字段 | 列 between 最小值 and 最大值
    between ... and ... 操做符的主要功能是針對於一個指定的數據範圍進行查找,在設置範圍的時候,能夠是數字、字符串或者是日期類型的數據。

    3.1 範圍查詢——範例
        使用between and 操做符查詢出工資範圍在1500到3000(包含)的所有僱員的信息。
            select * from emp where sal between 1500 and 3000;
        分析:
            對於這樣的操做,在以前能夠利用兩個關係運算符進行篩選,而有了between and以後,能夠直接篩選。

    3.2 範圍查詢——範例
        查詢在1981年僱傭的所有僱員的信息。
            select * from emp where hiredate between '01-1月 -81' and '31-12月 -81';
        分析:
            在這個查詢中必定會用到hiredate字段,可是如今並無學過有關於日期的處理,因此此時若是想要進行日期格式的編寫。
            由於Oracle出現年代的緣由,81僅表明1981,若是想表示2081,則須要寫2081。
            若是說是在1981年僱傭,那麼範圍就是:1981-01-01~1981-12-31,只須要利用between and 將這兩個日期設置在內便可實現。
            實際上這就實現了日期和字符串數據之間的自動轉換操做功能。

四、null判斷
    4.1 判斷內容是否爲null
        語法:
            判斷爲null:
                字段 | 值 is null;
            判斷不爲null:
                字段 | 值 is not null (not 字段 | 值 is null);
        null是一個未知的數據,因此對於null的處理,若是直接利用關係運算符判斷是沒法判斷的。

    4.2 範例——利用=進行null判斷
        select * from emp where comm = null;
        這時候不會有任何的結果返回,由於null不能使用=進行判斷。

    4.3 null判斷——範例
        查詢出全部領取佣金的僱員的完整信息。
        實現一:select * from emp where comm is not null;
        實現二:select * from emp where not comm is null;
        分析:
            佣金的字段是comm,領取佣金的概念就是佣金不爲null。

    4.4 null判斷——範例
        查詢全部不領取佣金的僱員的完整信息。
            select * from emp where comm is null;
        分析:
            不領取佣金那麼comm字段的內容就是null。
    4.5 null判斷——範例
        列出全部不領取佣金的僱員,並且同時要求這些僱員的基本工資大於2000的所有僱員的信息。
            select * from emp where comm is null and sal > 2000;

    4.6 null判斷——範例
        查詢沒有佣金或佣金低於100的員工。
            select * from emp where comm is null or comm < 100;

    4.7 null判斷——範例
        查詢收取佣金的員工的不一樣工做。
            select distinct job as 工做 from emp where comm is not null;
        分析:
            既然如今要找的是職位,那麼頗有可能出現重複的數據,重複的數據必須使用distinct去除。

五、列表範圍查找
    所謂的列表範圍指的是給了用戶固定的幾個參考值,只要符合這個值就知足條件。
    語法:
        在指定數據範圍內:字段 | 值 in(值,值,...)
        不在指定數據範圍內:字段 | 值 not in(值,值,...)

    5.1 範圍判斷——範例
        查詢出僱員編號是736九、778八、7566的僱員信息。
            select * from emp where empno in (7369,7788,7566);
        分析:
            那麼若是面對這樣的操做,若是此時不使用in操做符,能夠利用多個條件而且使用or進行鏈接。

    5.2 範圍判斷——範例
        查詢除了736九、778八、7566以外的僱員信息。
            select * from emp where empno not in(7369,7788,7566);
        分析:
            如今必定不在範圍以內,因此使用not in進行判斷。
        可是在使用not in操做時須要注意,關於null的問題:
            若是如今使用的是in操做符判斷的範圍數據之中包含了null,那麼不會影響最終結果。
            可是若是使用not in裏面包含null字段,結果就是沒有任何數據顯示。
         緣由:
            使用not in或in其目的是顯示部分數據內容,若是說如今有一列數據不可能爲null,而且not in裏面判斷null的條件知足了,那麼就表示查詢所有數據。

六、like模糊查詢
    語法:
        模糊查詢:字段 | 值 like 匹配標記
        不知足模糊查詢:字段 | 值 not like 匹配標記。
    若是如今想對某一列進行某查詢,能夠使用like子句完成,經過like能夠進行關鍵字的模糊查詢,like子句中有兩個通配符:
        百分號(%):
            能夠匹配任意類型和長度的字符,若是是中文則使用兩個百分號。
            出現0次1次或屢次。 
        下劃線(_):
            匹配單個任意字符,它經常使用來限制表達式的字符長度。
            出現1次。 

    6.1 模糊查詢——範例
        如今查詢出僱員姓名是以S開頭的所有僱員的信息。
            select * from emp where ename like 'S%';
        分析:
            在S以後的內容能夠是任意的數據,多是0位,多是1位,也多是多位。

    6.2 模糊查詢——範例
        查詢僱員姓名的第二個字母是M的所有僱員信息。
            select * from emp where ename like '_M%';
        分析:
            如今只是要求第二個字母,因此第一個字母能夠是任意數據,因此使用「_」佔位。

    6.3 模糊查詢——範例
        查詢出姓名中任意位置包含字母F的僱員信息。
            select * from emp where ename like'%F%';
        分析:
            如今多是開頭,也多是結尾,或者是在中間,因此就必需要考慮到先後都有的問題,那麼能夠使用「%」。

    6.4 模糊查詢——範例
        查詢僱員姓名長度爲6或者是超過6個的僱員信息。
            select * from emp where ename like '______%';
        分析:
            姓名的長度爲6個字符,那麼能夠寫6個下劃線,若是超過6個,能夠加一個「%」。

    6.5 模糊查詢——範例
        查詢出僱員基本工資中包含1或者是在81年僱傭的所有僱員信息。
            select * from emp where sal like'%1%' or hiredate like '%81%';
        分析:
            在以前的全部查詢中都是針對於字符數據進行操做,而對於like而言,也能夠在數字或者日期類型上使用。

        可是有一點須要提醒,若是在設置模糊查詢的時候不設置關鍵字,則表示查詢所有。
        這一種操做在往後的程序開發中也是很常見的(DAO設計模式)。

    6.6 模糊查詢——範例
        找出部門10種全部經理,部門20種全部的業務員,既不是經理又不是業務員可是其薪資大於等於2000的全部員工的詳細信息,而且要求這些員工的姓名之中包含字母S或字母K。
            select * from emp 
            where ((deptno = 10 and job = 'MANAGER') 
            or (deptno = 20 and job = 'CLERK')
            or (job not in('MANAGER','CLERK') and sal >= 2000)) 
            and (ename like '%S%' or ename like '%K%');
        分析:
            如今存在如下的幾個條件:
                條件一:10部門的經理。。
                條件二:20部門的業務員。
                條件三:不是經理和辦事員,可是工資大於或等於2000。
                條件四:以上全部的條件知足後再過濾,包含字母S或字母K。

七、小結
    7.1 限定查詢主要使用where子句,用於對選取的數據行進行控制。
    7.2限定查詢主要的運算符:關係運算符、between...and、in、is null、like


4、排序顯示
    要求:
        掌握order by子句的使用;
        掌握各個子句的執行順序。
    傳統數據查詢的時候,只會按照設置的主鍵進行排列,若是如今但願對指定的列進行排序操做,那麼就須要經過order by子句進行控制。
    語法:
        select [distinct] * | 列名 [as] 列別名,...
        from 表名
        [where 條件(s)]
        order by 排序的字段 | 列索引序號 asc|desc,排序的字段2 asc|desc....
    在order by子句之中能夠指定要進行排序的字段,有兩種排序模式:
        升序:ASC,默認
        降序:DESC,須要手動指定
    在全部的子句之中,order by子句是放在查詢語句的最後一行,是最後一個執行的:
        from -> where -> select -> order by。
    既然order by在select以後執行,那麼就表示order by子句能夠使用select子句中設置的別名了。
    1.1 數據排序——範例
        查詢僱員的完整信息而且按照基本工資由高到低進行排序。
            select * from emp order by sal desc;
        分析:
            如今是針對全部數據行進行排序,那麼直接執行order by子句便可。

    1.2 數據排序——範例
        按照基本工資由低到高進行排序。
            select * from emp sal;
            select * from emp sal asc;
        分析:
            以前使用的是一個降序排列,如今進行升序排列,默認使用asc,也能夠手動指定asc。

    1.3 數據排序——範例
        查詢出全部業務員的詳細資料,而且按照基本工資由低到高排序。
            select * from emp where job = 'CLERK' order by sal ;
        分析:
            如今再也不是針對全部的數據進行排序,須要對數據執行篩選,能夠使用where子句完成。

    1.4 數據排序——範例
        查詢全部僱員的信息,要求按照基本工資由高到低排序,若是工資相等則按照僱用日期進行排序,由早到晚排序。
            select * from emp order by sal desc,hiredate ;
        分析:
            如今的排序須要設置兩個排序的字段:sal(DESC),hiredate(ASC)
        本程序的語法沒有問題,有問題的是在於數據中,由於如今的數據都是後期處理的結果,若是想要正常的觀察數據,那麼能夠將數據庫切換到PDB之中,找到原始的scott用戶數據。

        對於排序,除了使用字段以外,也能夠設置序號,可是此操做不建議使用。
            select empno,ename,sal,job
            from emp
            order by  3;
        按照第三列進行排序。

二、小結
     使用order by子句能夠對查詢結果進行排序,order by子句必定要寫在全部查詢語句的最後。   

5、單行函數
    什麼是函數?
        函數就是和Java語言中的方法的功能是同樣的,都是爲了完成某些特定操做的功能支持。
        而在Oracle數據庫裏面也包含了大量的單行函數,掌握這些函數以後,能夠方便的幫助進行對數據庫相關的開發。
    對於開發者而言,最重要的就是SQL語法和單行函數。
    開發分爲兩種:
        SQL開發,數據庫和程序的結合。
        第二部分是函數,過程等開發(PL/SQL編程)。
    Oracle中的單行函數的數量是很是多的,本章只講解使用,後面會講解如何開發用戶本身的函數(PL/SQL編程)。

    語法:
        function_name(列 | 表達式[參數1,參數2]);
    單行函數主要分爲如下幾種:
        字符串函數:接收數據返回具體的字符信息。
        數值函數:對數字進行處理,例如四捨五入。
        日期函數:直接對日期進行相關的操做。
        轉換函數:日期、字符、數字之間能夠完成相互轉換功能。
        通用函數:Oracle提供的有特點的函數。

一、字符串函數
    字符串函數必定是以字符數據爲主。

    函數:
        1)upper(列 | 字符串)
            將字符串的內容所有轉換爲大寫。
        2)lower(列 | 字符串)
            將字符串的內容所有轉換爲小寫。
        3)initcap(列 | 字符串)
            將字符串的開頭首字母大寫。
        4)replace(列 | 字符串,舊字符串, 新字符串)
            使用新字符串替換舊字符串。
        5)length(列 | 字符串)
            求出字符串的長度。
        6)substr(列 | 字符串,開始點,[長度])
            截取字符串。
        7)ascii(字符)
            返回指與指定字符對應的ASCII碼。
        8)chr(數字)
            給出一個數字,並返回與之對應的字符。
        9)rpad(列 | 字符串,字符串長度,填充字符)
             lpad(列 | 字符串,字符串長度,填充字符)
            在左或右填充指定的字符,使字符串達到指定長度爲止。
        10)ltrim(字符串)
            rtrim(字符串)
            去掉左或右的空格。
        11)trim(列 | 字符串)
            去掉左右空格。
        12)instr(列 | 字符串, 要查找的字符串)
            查找一個子字符串是否在指定的位置上出現。

    1.1 字符串函數——範例
        這裏會出現一個問題,在Oracle裏面全部的驗證操做必須存在於完整的SQL語句之中,因此若是如今只是進行功能驗證,使用的是一張具體的表。
        驗證upper()和lower()函數。
            select upper('wangyanchao') from emp;
        會發現重複顯示14行‘WANGYANCHAO’,因此如今函數功能的確是成功驗證,可是代價過高。
        若是使用distinct能夠消除重複列,那麼emp表中的數據不少呢?那樣中間處理的數據量就會很大,因此如今就但願有一張表能夠幫助用戶進行驗證,而在Oracle中提供了一個dual的虛擬數據表。

    1.2 字符串函數——範例
        如今查詢出僱員姓名是‘smith’的完整信息,可是因爲失誤,沒有考慮到數據的大小寫問題(在一些項目的運行之中常常會出現此類輸入數據不考慮大小寫的問題),此時能夠使用upper()函數將內容所有變爲大寫。
            select * from emp where ename = upper('smith');
        在一些系統之中,有一些是不區分大小寫用戶名的,那麼他的作法就是將用戶註冊時全部資料都變爲了大寫或小寫。

    1.3 字符串函數——範例
        查詢全部僱員的姓名,要求每一個僱員的姓名以首字母大寫的形式出現。
            select initcap(ename) from emp;
        分析:
            首字母大寫,那麼其餘字母必定都是小寫,因此就能夠利用initcap()函數進行處理。

    1.4 字符串函數——範例
        查詢全部僱員的姓名,要求將全部僱員姓名中的字母「A」替換成字母「_」。
            select replace(ename,'A','_') from emp;
        分析:
            字符串的替換必定是replace()函數。

    1.5 字符串函數——範例
        查詢出姓名長度是5的全部僱員信息。
            select * from emp where length(ename) = 5;
        分析:
            若是想要計算長度,那麼使用的必定是length()函數,返回的長度必定是數值型數據。

    1.6 字符串函數——範例
        查詢出僱員姓名前三個字母是‘JAM’的僱員信息。
            select * from emp where substr(ename,0,3) = 'JAM';
        分析:
            如今須要截取前三個字符,那麼截取的操做必定就要使用substr()函數。
            substr()函數包含頭不包含尾。
            但是在截取以前,須要注意sunstr()函數有兩種形式:
                從指定位置截取到結尾:
                    substr(列 | 字符串, 截取開始位置);
                截取部分字符串:
                    substr(列 | 字符串, 截取開始位置,截取字符個數);
        須要注意的是,在Oracle數據庫之中,下標都是從1開始,若是設置爲0,那麼也會將其自動轉換爲1。

    1.7 字符串函數——範例
        查詢全部10部門的僱員,可是不顯示每一個僱員姓名的前三個字母。
            select substr(ename,3) from emp where deptno = 10;

    1.8 字符串函數——範例
        顯示每一個僱員姓名及其姓名的後三個字母。
            select substr(ename, length(ename)-2) from emp;
        也能夠在substr()函數中設置負數參數。
            select substr(ename, -3) from emp;
        分析:
            若是想要截取每一個姓名之中的後三個字符,首先要解決的問題就是開始位置,從一個指定的開始位置一直截取到結尾。
            但是每一個僱員的姓名長度並不相同,那麼開始位置如何肯定呢?
                實現一:使用傳統作法,先求得姓名長度,而後減去2來肯定開始位置。
                實現二:能夠設置開始位置爲負數,能夠理解爲從右開始計算。
        面試題:
            很明顯使用第二種方法最爲方便,這個也屬於Oracle的特點,不過須要注意的是:Java語言的字符串下標仍是從0開始,並且Java語言裏面的substring()方法是不可以設置負數的。

    1.9 字符串函數——範例
        返回指定字符的ASCII碼。
            select ascii('a') from dual;

    1.10 字符串函數——範例
        驗證chr()函數,將ASCII碼變回字符串。
            select chr(65) from dual;

    1.11 字符串函數——範例
        去掉字符串左邊的空格。
            select ltrim('             aa') from dual;
        rtrim()、trim()方法原理一致。
        中間空格沒法消除。

    1.12 字符串函數——範例
        字符串填充——lpad()、rpad()
             select lpad('aa',5,'*') from dual;
        也能夠rpad()和lpad()組合使用。
            select rpad(lpad('aa',5,'*'),8,'*') from dual;

    1.13 字符串函數——範例
        字符串查找——instr()。
            select instr('aaaaaabaaa','b') from dual;
        分析:
            若是查找到指定內容,那麼此函數就會返回該字符串首字符出現的位置,若是找不到,則返回0。
        這個函數和Java中的indexOf()方法功能是相同的。



二、數值函數
    要求:
        掌握數值函數的使用。

    函數:
        round(數字 [保留位數])
            對小數進行四捨五入,能夠指定保留位,若是不指定,則表示將小數點以後的數字所有進行四捨五入。
        trunc(數字[截取位數])
            保留指定爲數的小數,若是不指定,則表示不保留小數。
        mod(數字,數字)
            取模。

    2.1 數值函數——範例
        驗證round()函數。
            select round(789.652) from dual;
                若是不設置參數,則默認不保留小數位。
            select round(789.652,2) from dual;
                保留兩位小數。
            select round(789.652,-3) from dual;
                處理整數進位。

    2.2 數值函數——範例
        列出每一個僱員的一些基本信息和日工資狀況。
            select ename , round(sal/30,2) from emp;
        分析:
            對於日工資的計算能夠採用30天爲基礎,確定會有小數,就保留兩位。

    2.3 數值函數——範例
        驗證trunc()函數。
            select trunc(789.652) from dual;
                去掉小數,而且不進位。
            select trunc(789.652,2) from dual;
                保留兩位小數,而且不進位。

    2.4 數值函數——範例
        驗證mod()函數。
             select mod(10,3) from dual;
                10%3。

三、日期函數
    要求:
        掌握日期的數學計算操做。
        掌握日期函數的使用。

    函數:
        add_months(日期, 數字)
            在指定的日期上加入指定的月數,求出新的日期。
        months_between(日期1, 日期2)
            求出兩個日期間的僱傭月數。
        next_day(日期,星期數)
            求出下一個的星期X的具體日期。
        last_day(日期)
            求出指定日期的最後一天日期。
        extract(日期1, 日期2)
            日期時間分割,或計算給定兩個日期間隔的月數。

    若是如今想要進行日期的操做,那麼必定會存在一個前提:那就是必須知道當前日期。

    3.1 取得當前的系統時間
        在Oracle中,用戶能夠直接經過sysdate表示出當前的系統時間。
            select sysdate from dual;
        能夠直接利用sysdate僞列取得當前日期時間。
        所謂的僞列指的是否是表中的列,可是又能夠直接使用的列,叫作僞列。

    3.2 修改日期顯示格式
        alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
        select sysdate from dual;

    3.3 日期操做公式
        除了取得系統時間的操做以外,在Oracle中也有以下三個日期操做公式:
            日期 - 數字 = 日期;
                若干天前的日期。
            日期 + 數字 = 日期;
                若干天后的日期。
            日期 - 日期 = 數字(天數);
                兩個日期的時間差。

        絕對不會存在「日期 + 日期」的計算。

    3.4 日期函數——範例
        查詢出每一個僱員的到今天爲止的僱傭天數、以及十天前每一個僱員的僱傭天數。
            select round(sysdate - hiredate) as 僱傭天數, round(sysdate - 10 - hiredate) as 十天前僱傭天數  from emp;
        分析:
            若是要想計算天數惟一能夠使用的公式就是「日期1 - 日期2」,日期1確定是使用sysdate取得當前的日期,而日期2就是每一位僱員的hiredate。

        以上只是針對於當前日期的操做,而對於Oracle而言,也提供相應的日期函數,之因此使用日期函數,主要是爲了不閏年等問題。

    3.5 日期函數——範例
        驗證add_months()函數。
            select sysdate, add_months(sysdate, 3) as 三個月以後的日期, add_months(sysdate, -3) as 三個月以前的日期, add_months(sysdate, 60) as 六十個月以後的日期 from dual;
        分析:
            使用add_months()函數的主要功能是在一個指定日期上增長若干個月以後求得的日期。
            能夠使用負數。

    3.6 日期函數——範例
        要求顯示全部僱員在被僱傭三個月以後的日期。
            select EMPNO ,ENAME ,JOB ,HIREDATE ,
            add_months(hiredate,3) as 被僱傭三個月後的日期
            from emp;
        分析:
            須要查詢出每一位僱員在僱傭三個月以後的信息,能夠經過查詢hiredate來增長3個月獲得僱員信息。
        注意:別名長度不能超過30個字符。 

    3.7 日期函數——範例
        驗證next_day()函數,next_day()函數的功能是求出下一個指定的日期數,若是說如今的日期是「2012年01月30日 星期一」,那麼若是如今想要知道下一個星期一或星期日的具體日期,則能夠使用next_day()函數。
            select sysdate, next_day(sysdate,'星期一') as 下一個星期一日期 from dual;

    3.8 日期函數——範例
        驗證last_day()函數,使用last_day()函數能夠求得指定日期所在月的最後一天日期,若是今天的日期是「2016年01月19日」,則使用last_day()求出來的日期就是「2016年1月31日」。
            select last_day(sysdate) from dual;

    3.9 日期函數——範例
        查詢全部事在其僱傭所在月的倒數第三天被公司僱傭的完整僱員信息。
            select * from emp where last_day(hiredate)-2 = hiredate;
        分析:
            每一位僱員都有本身的僱用日期,阿麼如今要求查詢出僱用日期在本月倒數第三天僱傭的人。
            首先須要僱員僱用日期所在月的最後一天:last_day(hiredate);

    3.10 日期函數——範例
        還有一個函數稱爲months_between()函數,此函數的功能是取得兩個日期之間所通過的月份間隔。

        查詢出每一個僱員的編號、姓名、僱用日期、僱傭的月數及年份。
            select empno as 僱員編號, ename as 僱員姓名, hiredate as 僱傭日期, trunc(months_between(sysdate,hiredate)) as 僱傭月數, trunc(months_between(sysdate,hiredate)/12) as 僱傭年份 from emp;

        擴充:
            查詢出每一個僱員的編號、姓名、僱用日期、已僱傭的年數、月數、天數。

            對於本程序而言,必定是分步計算,並且有必定的難度,由於須要操做的準確性。
            例如:
                今天的日期是2016年8月24日,那麼MARTIN的僱用日期是1980-09-28,那麼這位僱員到今天爲止被僱傭了
            步驟一:
                求出年,年只須要依靠月就能夠計算出來。
                    select empno,ename,hiredate, trunc(months_between(sysdate,hiredate)/12) as 已僱傭年數 from emp;
            步驟二:
                求出月,計算年的時候存在小數,那麼這裏面的數據就是月,只須要求模便可獲得。
            步驟三:
                是針對於天的計算,由於如今已經計算出了年和月,因此天應該拋去年和月的數字信息。
                如今的問題是,若是想要計算天數,惟一知道的公式就是「日期1-日期2」,那麼日期1必定使用的是sysdate,而日期2(應該去掉年和月),能夠利用add_months()函數實現此功能。

    3.11 extract()函數
        在Oracle 9i以後增長了一個extract()函數,此函數的功能主要是從一個日期時間(date)或者是時間間隔(interval)中截取出特定的部分,此函數的使用語法以下所示:
            extract([year | month | day | hour | minute | second] | [timezone_hour | timezone_minute] | [timezone_region _ timezone_abbr] from [日期(date_value) | 時間間隔(interval_value)])

        範例:
            從日期時間中取出年、月、日數據。
                select extract(year from date '2016-08-25') years, extract(month from date '2016-08-25') months, extract(day from date '2016-08-25') days from dual;
        分析:
            如今是經過一個日期的字符串完成的,那麼也能夠利用當前日期完成(sysdate,systimestamp) 

    3.12 日期函數——範例
        從時間戳中取出年、月、日、時、分、秒。
            select extract(year from systimestamp) years, extract(month from systimestamp) months, extract(day from systimestamp) days, extract(hour from systimestamp) hours, extract(minute from systimestamp) minutes, extract(second from systimestamp) seconds from dual;

    3.13 日期函數——範例
        驗證to_timestame()函數。
        除了以上的操做以外,主要的功能是取得時間間隔,此處須要用到一個時間轉換函數:
            to_timestamp(),能夠將字符串轉換爲時間戳,並且此處的內容須要使用到部分的子查詢功能。 
                select extract(day from to_timestamp('2016-08-26 12:22:22','yyyy-mm-dd hh24:mi:ss') - to_timestamp('2015-08-26 13:33:33','yyyy-mm-dd hh24:mi:ss')) days from dual;

    3.14 小結
         日期能夠與數字進行運算。
         使用日期函數能夠解決閏年的問題。

四、轉換函數
    要求:
        掌握字符、數字、日期間的轉換操做。 
    在數據庫之中主要使用的數據類型:字符、數字、日期(時間戳),因此這三類數據類型之間就須要實現轉換操做,那麼這個就屬於轉換函數的功能。

    函數:
        to_char(日期 | 數字 | 列, 轉換格式)
            將指定的數據按照指定的格式變爲字符串。
        to_date(字符串 | 列, 轉換格式)
            將指定字符串按照指定的格式變爲date型。
        to_number(字符串 | 列)
            將指定的數據類型變爲數值類型。



    4.1 to_char()函數
        經過名稱就能夠發現,此函數的功能是將數據類型變爲字符串。
        在默認狀況下,若是查詢一個日期,則日期默認的顯示格式爲「31-01月 16」,而這樣的日期顯示效果確定不如常見的「2016-01-31」看起來舒服,因此此時就能夠經過to_char()函數對這個顯示的日期數據進行格式化(格式化以後的數據是字符串),可是若是要完成這種格式化,則首先須要熟悉一下格式化日期的替代標記。

    日期格式化標記:
        01)yyyy        完整的年費數字表示,年有四位,因此使用四個y。
        02)y,yyy       帶逗號的年。
        03)yyy          年的後三位。
        04)yy            年的後兩位。
        05)y              年的最後一位。
        06)year         年份的文字表示,直接表示四位的年。
        07)month      月份的文字表示,直接表示兩位的月。
        08)mm          用兩位數字來表示月份,月有兩位,因此使用兩個m。
        09)day          天數的文字表示。
        10)ddd          表示一年中的天數。
        11)dd             表示一月中的天數。
        12)d              表示一週中的天數。
        13)dy            用文字表示星期幾。
        14)ww           表示一年中的週數。
        15)w              表示一月中的週數。
        16)hh            表示12小時制,小時是兩位數字,因此使用兩個h。
        17)hh24        表示24小時制。
        18)mi             表示分鐘。
        19)ss             表示秒,秒是兩位數字,因此使用兩個s。
        20)sssss        午夜以後的秒數字表示(0~86399)
        21)fm             去掉查詢後的前導0,該標記用於時間模板的後綴。
        22)am | pm    表示上午或下午。

        格式化數字標記:
            1)9    表示一位數字。
            2)0    顯示前導0。
            3)$    將貨幣的符號顯示爲美圓符號。
            4)L    根據語言環境不一樣,自動選擇貨幣符號。
            5).     顯示小數點。
            6),     顯示千位符。

        在to_char()函數中,須要使用兩個參數:日期數據、轉換格式。
        select sysdate 當前系統時間, to_char(sysdate,'yyyy-mm-dd') 格式化日期, to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') 精確到秒, to_char(sysdate,'fmyyyy-mm-dd hh24:mi:ss') 去掉前導0 from dual;
        在開發之中建議不要取消前導0,由於會致使字符串長度發生變化。

        除了使用標記(使用標記是一種習慣,就像java.text.SimpleDateFormat類同樣),也能夠使用單詞來表示。
            select sysdate 當前系統時間, to_char(sysdate,'year-month-day')格式化日期 from dual; 

    4.2 轉換函數——範例
        查詢出全部在每一年2月份僱傭的僱員信息。
            select * from emp where '02' = to_char(hiredate,'mm')
            select * from emp where 2 = to_char(hiredate,'mm') 
         分析:
            能夠利用to_char()函數從僱用日期中去的僱傭的月份。

    4.3 轉換函數——範例
        將每一個僱員的僱用日期進行格式化顯示,要求全部的僱用日期能夠按照「年--月-日」的格式進行顯示,也能夠將僱傭的年、月、日拆開分別顯示。
            select ename,job,hiredate, to_char(hiredate,'yyyy-mm-dd'), to_char(hiredate,'yyyy') 年, to_char(hiredate,'mm') 月, to_char(hiredate,'dd') 日 from emp;

    4.4 to_char()函數格式化數字
        to_char()函數最爲要的功能是能夠將數字進行格式化。
        格式化數字顯示:
            select to_char(987654321.123,'999,999,999,999.9999') 格式化數字 from dual; 
            select to_char(987654321.123,'000,000,000,000.0000') 增長前導0 from dual; 
        除了對數字格式化,也能夠對貨幣進行顯示:
            select to_char(987654321.123,'l000,000,000,000.000000') 根據語言自動選擇貨幣, to_char(987654321.123,'$000,000,000,000.000000') 顯示美圓 from dual;

        在開發之中,to_char()函數的做用仍是很是明顯的,建議掌握。 

    4.5 to_date()函數
        這個函數主要是將字符串變爲日期類型數據,而改變的過程當中依然須要以前to_char()函數中使用的相關標記。
        字符串轉日期:
            select to_date('2016-08-27','yyyy-mm-dd') from dual;

    4.6 to_number()函數
        to_number()函數的做用就是將字符串變爲數字。

        使用to_number()函數將字符串變爲數字:
            select 18 + to_number('5') from dual;
        不使用to_number()函數字符串也能夠自動變爲數字:
            select 18 + '5' from dual;

        Oracle裏面支持數據類型的自動轉型。

    4.7 通用函數
        目標:掌握Oracle函數的使用。

        函數:
            1)nvl(數字 | 列, 默認值)
                若是現實的數字是null的話,則使用默認數值表示。
            2)nvl2(數字 | 列, 返回結果一(不爲空顯示), 返回結果2(爲空顯示))
                判斷指定的列是否爲null,若是不爲null則返回結果一,若是爲null則返回結果二。
            3)nullif(表達式一, 表達式二)
                比較表達式一和表達式二的結果是否相等,若是相等返回null,若是不相等返回表達式一。
            4)decode(列 | 值, 判斷值1, 顯示結果1, 判斷值2, 顯示結果2..., 默認值)
                多值判斷,若是某一個列(或某一個值)與判斷值相同,則使用指定的顯示結果輸出,若是沒有知足條件,則顯示默認值。
            5)case 列 | 數值 when 表達式1 then 顯示結果1... else 表達式n ... end
                用於實現多條件判斷,在when以後編寫條件,而在then以後編寫條件知足所進行的操做,若是都不知足,則使用else中的表達式處理。
            6)coalesce(表達式1, 表達式2, ... 表達式n)
                將表達式諸葛進行判斷,若是表達式1的內容是null,則顯示錶達式2,若是表達式2的內容是null,則顯示錶達式3,以此類推,若是表達式n的結果仍是null,則返回null。

        對於通用函數而言,只有兩個核心函數:nvl()、decode()。

    4.8 使用nvl()函數處理null。
        在數據庫之中,null是沒法進行計算的,即在一個數學計算之中若是存在了null,則最後的結果確定是null。
        要求查詢出每一個僱員的編號、姓名、職位、僱用日期、年薪。
            select empno,ename,job,hiredate,(sal+comm)*12 年薪 from emp;
        能夠發現,有的人年薪爲null,由於comm上的內容有的是null,而只要是null參與運算,則最終的結果都是null。
        因此在這種狀況下,就須要針對null進行處理,此時能夠將null轉變爲0。

        驗證nvl()函數:
            select nvl(null,0),nvl(3,0) from dual;
            這個時候發現若是爲null,那麼就將其變爲0,若是不是null,則繼續使用指定數值。
            select empno,ename,job,hiredate,(sal+nvl(comm,0))*12 年薪 from emp;

    4.9 nvl2()函數
        nvl2()函數是在Oracle 9i以後增長的一個新的功能函數,相比較nvl()函數,nvl2()函數能夠同時對null或非null進行不一樣的判斷並返回不一樣的結果。
        查詢每一個僱員的編號、姓名、年薪、基本工資、獎金。
            select empno, ename, nvl2(comm,(comm+sal)*12,sal*12),sal,comm from emp;

    4.10 nullif()函數
        nullif(表達式一, 表達式二)函數的主要功能判斷兩個表達式的結果是否相等,若是相等則返回null,不相等則返回表達式一。

        驗證nullif()函數:
            select nullif(1,1), nullif(1,2) from dual;

    4.11 decode()函數
        decode()函數是Oracle中最有特點的一個函數,decode()函數相似於程序中的if...else if...else,可是判斷的內容都是一個具體的值。
        decode()函數的語法以下:
            decode(列 | 表達式, 值1, 輸出結果, 值2, 輸出結果,...默認值)

        測試decode()函數:
            select decode(2,1,'內容爲1',2,'內容爲2'),decode (2,1,'內容爲1','沒有知足條件') from dual;

        在Java語言之中,if...else判斷的是邏輯條件,而在decode()函數中,判斷的是數值。

    4.12 decode()函數——範例
        如今僱員表中的工做有如下幾種:CLERK、SALESMAN、MANAGER、ANALYST、PRESIDENT。
        要求能夠查詢僱員的姓名、職位、基本工資等信息,可是要求將全部的職位信息都替換爲中文顯示。
            select ename,job,decode(job,'CLERK','業務員','SALESMAN','銷售人員','MANAGER','經理','ANALYST','分析員','PRESIDENT','總裁'),sal from emp;
        須要注意的是,若是使用decode()函數進行判斷,那麼全部的內容都要進行判斷,若是有記錄未進行判斷,那麼全部未判斷的內容都爲null。

    4.13 case表達式
        case表達式是在Oracle 9i版本引入的,功能與decode()函數有些相似,都是執行多條件判斷,不過嚴格來說case表達式自己並不屬於函數的範疇,它的主要功能是針對於給定的列或者字段進行依次判斷,在when中編寫判斷語句,而在then中編寫處理語句,最後若是都不知足,則使用else進行處理。

    4.14 case表達式——範例
        顯示每一個僱員的姓名、工資、職位,同時顯示新的工資(新工資的標準爲:業務員增加10%、銷售人員增加20%、經理增加30%、其餘職位的人增加50%)
            select ename,sal,case job when 'CLERK' then sal*1.1when 'SALSESMAN' then sal * 1.2when 'MANAGER' then sal * 1.3 else sal * 1.5 end 新工資 from emp;

    4.15 coalesce()函數
        coalesce(表達式1, 表達式2, 表達式3,...表達式n)函數的主要功能是對null進行操做,採用依次判斷表達式的方式完成,若是表達式1位null,則顯示錶達式2的內容,若是表達式2的內容爲null,則顯示錶達式3的內容,以此類推,判斷到最後若是仍是null,則最終顯示的結果就是null。 

        驗證coalesce()函數:
            select ename, sal, comm coalesce(comm,100,2000),coalesce(comm,null,null) from emp; 

        至關於if...else if...else if...else 

6、多表查詢
    對於查詢,以前已經學習過了簡單查詢、限定查詢、查詢排序,這些都屬於SQL的標準語句,在上一章的單行函數,主要功能是爲了彌補查詢的不足。
    而從多表查詢開始,就正式的進入到了複雜查詢部分。

    要求:
        多表查詢的主要目的。
        多表查詢的基本實現。
        理解笛卡爾積的概念與消除。

一、多表查詢的基本語法
    1.1 多表查詢語法
        多表查詢就是在一條查詢語句中,從多張表裏一塊兒取出所須要的數據,若是想要進行多表查詢,直接在from子句以後跟上表便可,此時的語法以下:
            select [distinct] * | 列名稱 [as] [列別名]....from 表名稱1, 表名稱2... where 條件(s) order by 字段
        在以前所編寫的全部查詢語句中,from子句以後只編寫了一個查詢表,而多表查詢就至關於在from子句以後編寫了多個查詢表,同時從多個表中獲取數據。

        下面就將採用emp表和dept表一塊兒進行多表查詢,可是在查詢以前,首先要作一個操做,那就是先肯定emp表和dept表中的數據量分別有多少,能夠使用count()函數完成這種操做。
            select count(*) from emp;
            select count(*) from dept;
        能夠發現emp和dept兩張數據表加在一塊兒才18行記錄,那麼下面就按照以前給出的語法格式,使用多表查詢,當數據量過大時會致使效率問題。

    1.2 使用多表查詢
        select * from emp,dept;
        發現此時的結果一共返回了56行記錄,那麼此時的問題就是笛卡爾積所形成的。

    1.3 笛卡爾積
        在進行多表鏈接查詢的時候,因爲數據庫內部的處理機制,會產生一些「無用」的數據,而這些數據就稱爲笛卡爾積。經過以前的查詢能夠發現,笛卡爾積返回的56條記錄恰好是14 * 4(emp表的記錄數 * dept表的記錄數)的運算就結果,即,同一條數據重複顯示了n次。

        能夠發現笛卡爾積的出現,會讓查詢結果變得很是的龐大,若是如今兩張表的數據量都很大,那麼這種龐大是很可怕的,因此如今必需要想辦法消除笛卡爾積。

    1.4 消除笛卡爾積
        通常而言,若是想要進行笛卡爾積的消除,每每會使用關聯字段,利用等值條件來處理笛卡爾積。
        因爲多張表之間可能會存在重名的字段,因此在進行重名字段訪問的時候,前面須要加上表名稱,採用「表名稱.字段」的方式來進行訪問。
            select * from emp,dept where emp.deptno = dept.deptno;
        這個時候的查詢結果能夠發現已經消除掉了笛卡爾積,可是這個時候笛卡爾積依然存在,只是不顯示了而已。

        已經清楚了基本概念以後,下面能夠針對數據量作一個分析,在Oracle中存在了sh用戶,此用戶保存在了PDBORCL插入式數據庫之中,能夠使用sh用戶中的sales表和costs表進行測試。
            select count(*) from sh.costs, sh.sales;
        一旦開始執行以後,那麼這個等待的過程會很長,可是這個時候是顯示數據量(包含笛卡爾積的數據量),然後再開始消除,利用等值關聯。
        返回的數據量:75448036416
        雖然消除掉了全部顯示的笛卡爾積,可是數據庫的原理機制就表示笛卡爾積永遠存在,笛卡爾積問題會隨着數據量的增大而愈加明顯。

    1.5 小結
        多表查詢會產生笛卡爾積,因此性能比較差。
        多表查詢時能夠利用等值關聯字段消除笛卡爾積。

二、多表查詢實例
    雖然多表查詢自己存在了性能問題,但並不表示多表查詢沒法使用,而是須要一些更合理的作法來解決多表查詢的問題,下面就針對多表查詢的使用與分析作一個習題講解。

    2.1 查詢每一個僱員的編號、姓名、職位、基本工資、部門名稱、部門位置信息。
        select emp.empno,emp.ename,emp.job,emp.sal,dept.dname,dept.loc from emp,dept where dept.deptno = emp.deptno;
        分析:
            肯定須要的數據表:
                emp表:查詢每一個僱員的編號、姓名、職位、基本工資。
                dept表:部門名稱、部門位置。
            肯定已知的關聯字段:
                部門與僱員關聯:emp.deptno = dept.depto;
            隨後還須要按照一個SQL語句的執行步驟編寫:from -> where -> select

        可是此處會發現一個問題,在上面的程序裏面,能夠發現採用了表名稱訪問的列名稱,那麼若是說如今的表名很長,會影響代碼閱讀性,因此每每在多表查詢的時候爲查詢的數據表定義別名,而別名也是在from子句中定義的。
            select E.empno,E.ename,E.job,E.sal,D.dname,D.loc from emp E,dept D where D.deptno = E.deptno;
        在之後的程序編寫中幾乎都會使用到別名。

    2.2 多表查詢——範例
        查詢出每一個僱員的編號、姓名、僱傭日期、基本工資、工資等級。
            select E.empno,E.ename,E.hiredate,E.sal,S.grade from emp E, salgrade S where E.sal between S.losal and S.hisal;
         分析:
            肯定所須要的數據表:
                emp表:每一個僱員的編號、姓名、僱用日期、基本工資。
                salgrade表:工資等級。
            肯定已知的關聯字段:
                emp.sal between salgrade.losal and salgrade.hisal;

    2.3 多表查詢——範例
        在以前的查詢中,發現只是顯示了數字一、二、三、四、5,如今但願能夠將其替換爲中文(若是須要替換功能,確定使用decode()函數)。
            select E.ename,E.job,E.sal,decode(S.grade,1,'E等工資',2,'D等工資', 3,'C等工資', 4,'B等工資', 5, 'A等工資') from emp E, salgrade S where E.sal between S.losal and S.hisal;

        以上的練習都是針對於兩張表進行的多表查詢,並且多表查詢中都只使用了一個條件來消除笛卡爾積。

    2.4 多表查詢——範例
        查詢出每一個僱員的姓名、職位、基本工資、部門名稱、工資等級。
            select E.ename,E.job,E.sal,D.dname,decode(S.grade, 5, 'E等工資', 4, 'D等工資', 3, 'C等工資', 2, 'B等工資', 1, 'A等工資') from emp E, salgrade S,dept D where E.sal between S.losal and S.hisal and E.deptno = D.deptno;
        分析:
            肯定所須要的數據表:
                emp表:每一個僱員的姓名、職位、基本工資。
                dept表:部門名稱。
                salgrade表:工資等級。
            肯定已知的關聯字段:
                僱員表和部門表:emp.deptno = dept.deptno;
                僱員和工資等級:emp.sal between salgrade.losal and salgrade.hisal;
            若是如今是多個消除笛卡爾積的條件,那麼每每使用and將這些條件鏈接在一塊兒。 

        步驟一:查詢出僱員的姓名、職位、工資,這一操做只需單張數據表就能夠完成。
            select ename,job,sal from emp E;
        步驟二:加入部門表,確定須要一個字段與emp表一塊兒消除笛卡爾積。
            select E.ename,E.job,E.sal,D.dname from emp E, dept D where E.deptno = D.deptno;
        步驟三:加入工資等級表。
            如今已經取出三張表的笛卡爾積了,只須要經過一個條件將錯誤的笛卡爾積去除便可。
            select E.ename,E.job,E.sal,D.dname,S.grade from emp E, dept D, salgrade S where E.deptno = D.deptno and E.sal between S.losal and S.hisal;

        只要增長一張表,那麼就必定須要一個能夠消除增長表所帶來的笛卡爾積的問題。

    2.5 小結
        多表查詢之中,每增長一個關聯表都須要設置一個消除笛卡爾積的條件。

三、表的鏈接操做
    要求:
        清楚表的鏈接操做:內鏈接、外鏈接。

    對於數據表的鏈接操做在數據庫之中一共定義了兩種:
        內鏈接:
            也稱爲等值鏈接(或稱爲鏈接,還能夠被稱爲普通鏈接或者天然鏈接),是最先的一種鏈接方式,內鏈接是從結果表中刪除與其餘被鏈接表中沒有匹配的全部行,因此當匹配條件不知足時內鏈接可能會丟失信息。在以前所使用的鏈接方式都屬於內鏈接,而在where子句中設置的消除笛卡爾積的條件就是採用了等值判斷的方式進行的。
        外鏈接:
            內鏈接中只可以顯示等值知足的條件,若是不知足的條件則沒法顯示,若是如今但願特定表中的數據能夠所有顯示,就利用外鏈接,外鏈接分爲三種:左外鏈接(左連接)、右外鏈接(右鏈接)、全外鏈接(全鏈接)。

    3.1 操做準備——擴充數據
        爲了更好的觀察並理解各個鏈接方式的區別,那麼首先須要在emp表中增長一條數據。(沒有部門編號)
            insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values (8888,'王彥超','MANAGER', 7269,sysdate,8000, 1000,null);
        此時增長完的數據是一個沒有部門的僱員,即部門編號爲null,那麼下面就演示等值鏈接帶來的效果。

    3.2 使用等值鏈接
        select * from emp E, dept D where E.deptno = D.deptno; 
        經過查詢能夠發現:
            沒有部門的僱員沒有顯示。
        因此如今就能夠發現,使用內鏈接只有知足鏈接條件的數據纔會所有顯示。但是若是如今但願emp表或者dept表中的數據顯示完整,
 那麼久能夠利用外鏈接進行,而外鏈接如今主要使用兩種:
            左外鏈接:左關係屬性 = 右關係屬性(+),加號放在了等號的右邊,表示左鏈接。
            右外鏈接:左關係屬性 = 右關係屬性,放在了等號的左邊,表示右鏈接。
        在Oracle中能夠利用其提供的"(+)"進行左外鏈接或右外鏈接的實現。

    3.3 左外鏈接——範例
        顯示僱員編號是8888的僱員信息。
            select * from emp E, dept D where E.deptno = D.deptno(+);
        能夠顯示僱員編號爲8888的僱員信息。
    3.4 右外鏈接——範例。
        使用右外鏈接查詢emp表dept表。
            select * from emp E, dept D where E.deptno(+) = D.deptno;
        由於沒有部門編號爲40的僱員信息,因此dept.empno = 40這條記錄爲null。 

        何時使用外鏈接?
            若是所須要的數據信息未顯示,那麼就使用外鏈接,而具體是左外鏈接仍是右外鏈接,沒有必要去記,試一試便可。

四、自身關聯
    要求:
        掌握自身關聯的實現。
        理解外鏈接在自身關聯操做上的使用。

    4.1 自身關聯——範例
        查詢出僱員對應的領導信息。
        在emp表中存在一個mgr字段,這個字段表示的是僱員的領導。
            select E.empno,E.ename,E.mgr,M.empno,M.ename,M.mgr from emp E, emp M where E.empno(+) = M.mgr;
        分析:
            肯定所須要的數據表:
                emp表:僱員的編號、姓名。
                emp表:領導的編號、姓名。 
            肯定已知的關聯字段:
                僱員和領導:emp.mgr = memp.empno(僱員的領導編號 = 領導的信息--僱員信息)

        步驟一:
            直接進行自身鏈接的操做。
                select E.empno, E.ename, M.empno, M.ename from emp E, emp M where E.empno = M.mgr;
            如今的表中一共存在了15條記錄,可是卻只顯示了14條記錄,等值鏈接在沒有條件知足的時候,是不會顯示數據的。
            在emp表中的king僱員是沒有領導的,因此king的mgr爲null,那麼這個時候就必須考慮外鏈接。
        步驟二:
            使用左外鏈接:
                select E.empno, E.ename, M.empno, M.ename from emp E, emp M where E.mgr= M.empno(+);
            對於沒有領導信息的僱員,對應的領導信息所有使用null進行表示。

    4.2 多表查詢——範例
        查詢出在1981年僱傭的所有僱員的編號、姓名、僱傭日期(按照年-月-日的格式顯示)、職位、領導姓名、僱員月工資、僱員年薪(基本工資+獎金)、僱員工資等級、部門編號、部門名稱、部門位置,而且要求這些僱員的月基本工資在1500~3000之間,
將最後的結果按照年薪進行降序排列,若是年薪相同,則按照職位進行排序。
            select E.empno, E.ename, to_char(E.hiredate, 'yyyy-mm-dd'),E.job, M.ename, E.sal,nvl2(E.comm,(E.comm+E.sal)*12, E.sal * 12) as 年薪,S.grade,decode(S.grade, 1,'E等工資',2,'D等工資',3,'C等工資',4,'B等工資',5,'A等工資') as 工資等級,E.empno,D.dname,D.loc from emp E, emp M, salgrade S, dept D where E.deptno = D.deptno and E.mgr = M.empno(+) and E.sal between S.losal and S.hisal and E.sal between 1500 and 3500 and extract(year from E.hiredate) = '1981' order by 年薪 desc, job;

        分析:
            肯定所須要的數據表:
                emp表:編號、姓名、僱用日期、職位、月工資、計算年薪。
                emp表:領導姓名。
                dept表:部門編號、名稱、位置。
                salgrade表:工資等級。
            肯定已知的關聯字段:
                僱員和領導:emp.mgr = memp.empno;
                僱員和部門:emp.deptno = dept.deptno;
                僱員和工資等級:emp.sal between salgrade.losal and salgrade.hisal;
            步驟一:
                查詢出全部在1981年僱傭的僱員編號、姓名、僱用日期、職位、月工資、年薪,而且月薪在1500~3500之間,只須要emp一張表便可實現。
                    select E.empno,E.ename,E.hiredate,E.sal, (E.sal+nvl(E.comm,0))*12 income from emp E where to_char(E.hiredate,'yyyy') = '1981' and E.sal between 1500 and 3500;


            步驟二:
                加入領導信息,使用自身關聯。
                    select E.empno,E.ename,E.hiredate,E.sal, (E.sal+nvl(E.comm,0))*12 income from emp E, emp M where to_char(E.hiredate,'yyyy') = '1981' and E.sal between 1500 and 3500 and E.mgr = M.empno(+);

            步驟三:
                加入部門信息。
                     select E.empno,E.ename,E.hiredate,E.sal, (E.sal+nvl(E.comm,0))*12 income, D,deptno, D.dname, D.loc from emp E, emp M, dept D where to_char(E.hiredate,'yyyy') = '1981' and E.sal between 1500 and 3500 and E.mgr = M.empno(+) and E.deptno = D.deptno;

            步驟四:
                查詢出工資等級。
                    select E.empno,E.ename,E.hiredate,E.sal, (E.sal+nvl(E.comm,0))*12 income, D,deptno, D.dname, D.loc, S.grade from emp E, emp M, dept D, salgrade S where to_char(E.hiredate,'yyyy') = '1981' and E.sal between 1500 and 3500 and E.mgr = M.empno(+) and E.deptno = D.deptno and E.sal between S.losal and S.hisal;

            步驟五:
                進行排序。
                    由於select子句是在order by子句以前執行,因此在select子句之中所定義的別名在order by之中是能夠使用的。
                 select E.empno,E.ename,E.hiredate,E.sal, (E.sal+nvl(E.comm,0))*12 income, D,deptno, D.dname, D.loc, S.grade from emp E, emp M, dept D, salgrade S where to_char(E.hiredate,'yyyy') = '1981' and E.sal between 1500 and 3500 and E.mgr = M.empno(+) and E.deptno = D.deptno and E.sal between S.losal and S.hisal order by income desc, E.job;

        經過這一稍微複雜點的題目,能夠發現,全部的分析必需要分步進行,而這些分析過程,須要大量的練習才能夠鞏固。 

    4.3 小結
        自身關聯屬於一張表本身關聯本身的狀況,此時依然會產生笛卡爾積。

五、SQL1999語法
    要求:
        理解交叉鏈接的做用;
        理解天然鏈接的做用;
        理解on子句的使用;
        理解using子句的使用;
        理解外鏈接的使用。

    爲何會有SQL1999語法?
        在以前使用的"(+)"標記只適合在Oracle數據庫之中使用,若是是其餘數據庫,則沒法使用。
        可是大部分數據庫都是支持SQL1999語法。

    SQL1999語法:
        select [distinct] * | 列名稱[as][列別名]....from 表1 表1別名[cross join 表2 表2別名]
        [natural join 表2 表2別名] | [join 表2 using(關聯列名稱)] | [join 表2 on(關聯條件)] | [left | right | full outer join 表2 on(關聯字段)] [where 條件(s)] [order by 排序的字段1 asc | desc, 排序的字段2 asc | desc...];

    5.1 交叉鏈接
        交叉鏈接(cross join)做用於兩個關係上,而且第一個關係的每一個元組與第二個關係的全部元組進行鏈接,這樣的操做形式與笛卡爾積是徹底相同的,交叉鏈接的語法以下所示:
            select [distinct] * | 列名稱 from 表1 [cross join 表2] where 條件(s)

            select * from emp cross join dept;
        交叉鏈接的主要功能就是產生笛卡爾積。
        通常而言,在進行多表鏈接的時候都會存在關聯字段以消除笛卡爾積,而關聯字段的名稱通常都會同樣,若是不同,也會有部分相同,如今討論的是徹底同樣的狀況,例如deptno字段就是相同的,能夠利用天然鏈接來消除這個笛卡爾積。

    5.2 天然鏈接
        天然鏈接(natural join)運算做用於兩個關係,最終會經過兩個關係產生出一個關係做爲結果,與交叉鏈接(笛卡爾積)不一樣的是,天然鏈接只考慮那些在兩個關係模式中都出現的屬性上取值相同的元組對,天然鏈接的操做語法以下:
            select [distinct] * | 列名稱 from 表1 [natural join 表2] where 條件(s)

            select * from emp natural join dept;
        這個時候會把連接的字段放到第一列進行顯示,這種方式就是內鏈接的一種方式。
        此時沒有部門的僱員未出現。

    5.3 using子句
        經過天然鏈接能夠直接使用關聯字段消除笛卡爾積,那麼若是如今的兩張表中沒有存在這種關聯字段的話,就能夠使用using子句完成笛卡爾積的消除,using子句的語法以下所示:
            select [distinct] * | 列名稱 from 表1 join 表2 using(關聯列名稱)

            select * from emp join dept using(deptno);
        當表2中無指定字段時,會報錯。

    5.4 on子句
        在編寫等值鏈接時,採用了關鍵字段進行笛卡爾積的消除,那麼用戶在SQL:1999語法之中經過on子句就能夠由用戶手動設置一個關聯條件,on子句語法以下:
            select [distinct] * | 列名稱 from 表1 join 表2 on(關聯條件) where 條件(s)
        using是設置鏈接字段,而on是設置鏈接條件。
            select * from emp E join salgrade S on(E.sal between S.losal and S.hisal);

    5.5 外鏈接
        在數據庫的查詢操做中的外鏈接一共分爲三種形式:左外鏈接、右外鏈接、全外鏈接,而此時鏈接的語法以下:
            select [distinct] * | 列名稱 from 表1 [left | right | full outer join 表2 on(關聯條件)] where 條件(s);
        對於外鏈接,在以前使用的是"(+)"來完成的,這個標記只可以實現左外鏈接或者是右外鏈接,可是對於全外鏈接沒法使用,而全外鏈接只可以依靠SQL:1999語法中規定的內容來完成。

    5.6 右外鏈接
       select * from emp E right outer join dept D on(E.deptno = D.deptno);
       右外鏈接以右表爲主,因此部門表信息所有顯示。

    5.7 左外鏈接
        select * from emp E left outer join dept D on(E.deptno = D.deptno);
        左外鏈接以左表爲主,因此僱員表信息所有顯示。 

    5.8 全外鏈接
        select * from emp E full outer join dept D on(E.deptno = D.deptno);
        部門信息和僱員信息所有顯示。
        經過以上的分析能夠發現,給出的全部語法裏,只有全外鏈接是必須經過SQL:1999語法來實現的,可是對於全外鏈接,使用的狀況並很少。
        建議:若是使用的是Oracle數據庫,就使用"(+)"標記來控制左右鏈接,不使用它實現內鏈接。 

    5.9 小結
        交叉鏈接會產生笛卡爾積。
        天然鏈接能夠自動匹配關聯字段來消除笛卡爾積。
        若是要實現全外鏈接只可以依靠SQL:1999語法。

        SQL:1999語法能不用就不用。 

六、數據的集合運算
    數據的集合操做是對查詢結果的操做。
    要求:
        理解數據的集合操做:union、union all、intersect、minus

    6.1 集合運算
        集合運算時一種二目運算符,一共包括四種運算:並、差、交、笛卡爾積,其中對於笛卡爾積在以前已經瞭解過,因此本次主要是看並、交、差三種操做,操做集合的語法以下所示:
            查詢語句
                [union | union all | intersect | minus]
            查詢語句
                .....
        要實現集合的運算,主要使用四種運算符:
            union(並集):返回若干個查詢結果的所有內容,可是重複元組不顯示。
            union all(並集):返回若干個查詢結果的所有內容,重複元組也會顯示。
            minus(差集):返回若干個查詢結果中的不一樣部分。
            intersect(交集):返回若干個查詢結果中相同的部分。

    6.2 並集操做
        並集操做是將多個查詢的結果鏈接到一塊兒,而對於並集操做提供了兩種操做符:
            union(不重複顯示)
            union all(重複顯示)
        分析:
            select * from dept;
                這個查詢會返回四行記錄。
            select * from dept where deptno = 10;
                這個查詢會返回一行記錄。
            這個時候兩個查詢結果返回的列的結構相同,下面使用union鏈接:
                select * from dept
                    union
                select * from where deptno = 10;
                這時候只返回四行記錄,由於第一個查詢已經包含了第二個查詢的內容,因此重複數據就不顯示了,若是想要顯示重複的數據,能夠使用union all進行操做。
        提示:
            在之後進行查詢操做編寫過程當中,儘可能使用union或union all來代替or。
            範例:
                查詢全部銷售人員和業務員的信息。
                    select * from emp where job = 'SALESMAN' or job = 'CLERK'
                    或者:select * from emp where job in('SALESMAN','CLERK');
                改成union操做:
                    select * from emp where job = 'SALESMAN' union select * from emp where job = 'CLERK';
                    這樣寫至關於進行了兩個單表查詢,單表查詢性能相對較高。

    6.3 差集操做
        使用minus進行差集操做。
            select * from dept minus select * from dept where deptno = 10;
    6.4 交集操做
        使用intersect執行交集操做。
            select * from dept intersect select * from dept where deptno = 10;

    6.5 小結
        開發之中建議使用union來代替or操做。
        集合操做時,各個查詢語句返回的結構要求一致。


7、分組統計查詢
    要求:掌握各個統計函數的使用。

    一、統計函數
    在以前使用過count()函數,此函數的功能是能夠取得一張表中的所有數據量,而這種函數在數據庫之中就將其定義爲統計函數,有些地方也將其稱爲分組函數。

    統計函數:
        count(* | [distinct] 列)    求出所有的記錄數。
        sum(列)                        求出總和,操做的列是數字。
        avg(列)                         求平均值。
        max(列)                        求最大值。
        min(列)                         求最小值。
        median(列)                   返回中間值。
        variance(列)                 返回方差。
        stddev(列)                    返回標準差。
    給出了八個統計函數,可是從SQL的標準規定來說,只有5個是標準函數:count()  sum()  avg()  max()  min()

    1.1 統計函數——範例
        查找出公司每月支出的月工資的總和。
            select sum(sal) from emp;
        分析:求和使用sum()函數,工資使用sal列。

    1.2 統計函數——範例
        查詢出公司的最高工資、最低工資和平均工資。
            select max(sal), min(sal), avg(sal) from emp

    1.3 統計函數——範例
        統計出公司最先僱傭和最晚僱傭的僱用日期。
            select min(hiredate), max(hiredate) from emp;
        針對於僱用日期的查詢確定是用hiredate字段,在Oracle中的函數是能夠進行數據類型的互相轉換的。
        最先僱傭的hiredate的值必定是最小的,最晚僱傭的hiredate的值必定是最大的。

    1.4 統計函數——範例
        統計公司工資之中的中間值。
            select median(sal) from emp;
        分析:
            例如如今有三個數字:2000、3000、2500,所謂的中間值就是2500。

    1.5 統計函數——範例
        統計工資的標準差與方差。
            select stddev(sal) 標準差, variance(sal) 方差 from emp;

    1.6 統計函數——範例
        統計出公司的僱員人數。
            select count(*), count(empno), count(comm) from emp;
        分析:
            對於count()函數而言,是最先使用的函數,count()函數之中的參數能夠使用*,也能夠使用字段。
            如今empno字段上是不存在null值的。

    1.7 統計函數——範例
        驗證count(*)、count(字段)、count(distinct 字段)的使用區別。
            select count(*), count(ename), count(comm), count(distinct job) from emp;
        分析:
            對於count()函數,能夠傳遞三類參數:*、字段、distinct 字段。
        面試題:
            請問count(*),count(字段),count(distinct 字段)有什麼區別?
                在使用count(字段)的時候,若是列上存在了null,那麼null是不會進行統計的,例如count(comm)。
                在使用count(distinct 字段)的時候,若是有重複的記錄,也不會統計。
                在使用count(*)是最方便的,可是在開發時建議使用字段,避免使用*。
        在全部的統計函數中,只有count()函數能夠在表中沒有任何數據的時候依然返回內容,全部函數在沒有數據的時候返回的都是null,而count()函數返回的是數字0,因此count()函數永遠會返回數據,而這個問題也就至關於解決了JavaEE開發之中編寫DAO時,取得數據個數不用if判斷的緣由所在了。

    1.8 小結
        五個核心的統計函數:count()  avg()  sum()  min()  max()

二、單字段分組統計
    對於統計函數而言,單獨使用的狀況必定是有的,例如:在作報表顯示的時候基本的分頁操做,必定須要查詢出所有的數據量。
    要求:
        掌握group by子句的使用。
        掌握分組操做的使用限制。

    何時須要分組?
        對於分組這個概念在生活中每每會出現如下的需求:
            需求一:在一個班級之中,要求男女各一組進行辯論賽。
            需求二:在公司中,要求每一個部門一組進行拔河比賽。
    對於以上的兩個需求,假設存在學生表,那麼在學生表之中必定會存在一個性別的字段,而每個部門的數據,其性別內容必定是相同的。而在公司之中,若是要進行部門的分組,確定是須要有一個部門列的內容存在重複(部門,10部門,20部門)。


    能夠直接查看emp表之中的所有數據,以肯定是否存在能夠分組的字段。
        select * from emp;
    此時的查詢結果之中,job和deptno字段都出現了重複的內容。雖然在大部分狀況下(正常狀況)都容許在重複數據的列上實現分組,可是也容許一條數據一組。

    2.1 分組統計語法
        select [distinct] 分組字段 | 統計函數 from 表1 where 條件(s) group by 分組字段 order by 排序字段
        分組使用group by子句,可是此時select子句裏容許出現的就是分組字段和統計函數,這些在講解分組限制的時候再進行討論。

    2.2 分組統計——範例
        統計出每一個部門的人數。
            select deptno,count(*) from emp group by deptno;
        分析:
            此時必定須要按照部門的信息分組,在emp表中,每一個僱員的部門都使用一個部門編號來表示,那麼就能夠針對於deptno字段來實現分組統計。

    2.3 分組統計——範例
        統計出每種職位的最低和最高工資。
            select job,min(sal), max(sal) from emp group by job;
        分析:
            職位的信息統計,那麼確定按照job的字段實現分組,然後統計的函數確定是max()和min()。

        以上的兩個基本範例就實現了分組的基本操做,並且這些代碼都按照標準格式進行了編寫。
        但是在分組之中,最麻煩的地方就在於分組操做的若干個限制。

    2.4 分組注意事項
        1)若是沒有group by子句,則在select子句之中只容許出現統計函數,其餘任何字段都不容許出現。
            錯誤:select deptno, count(empno) from emp;
            正確:select count(empno) from emp;
        2)在統計查詢之中,select子句後只容許出現分組字段和統計函數,而其餘的非分組字段不能使用。
            錯誤:select deptno,ename,count(empno) from emp group by deptno;
            正確:select deptnom count(empno) from emp group by deptno;
            原則:在進行分組操做的時候,只有在group by子句之中出現的字段纔是在select子句中容許出現的字段,若是沒有在group by中出現,那麼select子句中也不容許出現。
        3)統計函數容許嵌套使用,可是嵌套統計函數以後的select子句之中不容許再出現任何的字段,包括分組字段。
            錯誤:select deptno,max(avg(sal)) from emp group by deptno;
            正確:select max(avg(sal)) from emp group by deptno;
            見範例。
    2.5 分組統計——範例
        求出每一個部門平均工資最高的工資。
        分析:
            按照部門分組,而後統計出每一個部門的平均工資數值,那麼針對於這些統計的結果求出一個最大的記錄。 
            錯誤代碼:
                select deptno, avg(sal) from emp group by deptno;
                此時計算出來的是每一個部門的平均工資,但是如今要求是統計出最高的平均工資。
                select deptno,max(avg(sal)) from emp group by deptno;    // ORA:00937:不是單組分組函數。
                此時由於select子句之中存在了deptno的字段,因此這個時候就出現了錯誤。
            正確代碼:
                select max(avg(sal)) from emp group by deptno;

    2.6 分組統計——範例
        查詢每一個部門的名稱、部門人數、部門平均工資、平均服務年限。
        分析:
            平均服務年限須要計算出年的概念,能夠使用months_between()函數,可是在這個查詢裏面確定須要不止一張表。
                1)肯定所須要的數據表:
                    emp表:部門人數、平均工資、平均服務年限,這些須要經過統計函數進行計算。
                    dept表:部門名稱。
                2)肯定已知的關聯字段:
                    emp表與dept表關聯:emp.deptno = dept.deptno
            步驟一:將dept表和emp表一塊兒進行查詢,暫時不進行分組操做。
                查詢每一個部門的名稱、僱員編號、工資、僱用日期。面試

                    select D.dname,D.deptno,E.sal,E.hiredate from emp E, dept D where E.deptno = D.deptno;

            步驟二:觀察如今的dname字段(查詢結果,理解爲臨時表),既然重複的的列就能夠進行分組。
                一旦出現分組以後,那麼未分組的字段內容就要變動了,變爲一條記錄,也就是說要出現分組字段和統計函數。
                select D.dname, count(E.empno), round(avg(months_between(sysdate,E.hiredate)/12),2), round(avg(E.sal),2) from emp E, dept D where E.deptno = D.deptno group by D.dname;
                注意:必須使用avg()函數來計算hiredate,由於使用group by子句以後,必須使用分組字段和聚合函數。 
             步驟三:此時出現的信息只包含了三個部門的信息,此時須要加入外鏈接來顯示40部門的信息。
                select D.dname, count(empno), round(avg(E.sal),2), round(avg(months_between(sysdate,E.hiredate)/12),2) from emp E, dept D where D.deptno = E.deptno(+) group by D.dname; 
                學到此處應該清楚的認識到group by子句的使用,那麼既然有了group by子句,也就有了一個新的子句執行順序:
                    from --> where --> group by --> select --> order by

    2.7 分組統計——範例
        查詢出公司各個工資等級僱員的數量、平均工資。
            select S.grade, count(empno),round(avg(E.sal),2) from emp E, salgrade S where E.sal between S.losal and S.hisal group by S.grade;
        分析:
            肯定所須要的數據表:
                emp表:僱員的數量、平均工資的統計結果。
                salgrade表:工資等級信息。
            肯定已知的關聯字段: 
                僱員與工資等級:emp.sal between salgrade.losal and salgrade.hisal;
            步驟一:將需求更改,使用salgrade表和emp表進行關聯查詢,查詢出等級編號、僱員編號和工資。
                select S.grade,E.empno, E.sal from salgrade S, emp E where E.sal between S.losal and S.hisal;
            步驟二:既然grade字段上有了重複的數據信息,那麼就能夠繼續針對於臨時表數據實現分組。
                select S.grade, count(empno),round(avg(E.sal),2) from emp E, salgrade S where E.sal between S.losal and S.hisal group by S.grade;

        以上的兩個範例是針對於多表查詢後的數據進行統計,實際上是根據返回結果(臨時表)進行分組。
        只要是行和列的組成結果必定是臨時表。

    2.8 分組統計——範例
        統計出領取佣金與不領取佣金的僱員的平均工資、平均僱傭年限、僱員人數。
            select '領取佣金', round(avg(sal),2) 平均工資, round(avg(months_between(sysdate,hiredate)/12),2) 平均年限, count(empno)sql

from emp where comm is not null
                union
            select '不領取佣金',round(avg(sal),2) 平均工資, round(avg(months_between(sysdate,hiredate)/12),2) 平均年限, count(empno)
from emp where comm is null 

        若是看到此範例要求,很明顯首先應該想到的是使用comm字段進行分組。
            group by comm;
        可是並無所謂的經過comm字段進行分組,而是按照每一種可能出現的佣金數值來進行的分組操做。
        下面須要更換一種思路:
            首先查詢出全部領取佣金的僱員信息。
                select '領取佣金', round(avg(sal),2) 平均工資, round(avg(months_between(sysdate,hiredate)/12),2) 平均年限, count(empno)from emp where comm is not null
            而後查詢出全部不領取佣金的僱員信息。
                select '領取佣金', round(avg(sal),2) 平均工資, round(avg(months_between(sysdate,hiredate)/12),2) 平均年限, count(empno)from emp where comm is null 
            以上的兩個查詢結果的返回列形式徹底相同,能夠使用union聯合查詢。

        拿到題目以後,必定要分析一下,不要按照習慣性的方式進行分組操做,並非全部的分組統計都須要group by子句。

    2.9 小結
        利用group by子句能夠設置分組。
        select子句在分組操做中的限制。

三、多字段分組統計
    在進行字段分組的時候無論是單表取數據,仍是多表查詢,那麼在一個列上必定會存在重複記錄。
    多字段分組對於單字段分組,本質上講沒有區別,也就是說若干個列上的數據同時存在重複,此時才須要多字段分組。

    要求:
        在group by子句中使用多個字段進行分組實現。

    語法:
        select [distinct] 分子字段1, 分組字段2.... from 表1,表2 where 條件(s) group by 分組字段1, 分組字段2
    既然能夠在group by子句中出現多個分組字段,那麼在select子句中也必定能夠定義多個分組字段的查詢顯示。

    3.1 分組統計——範例
        查詢每一個部門的詳細信息,包括部門編號、部門名稱、部門地址、部門人數、部門平均工資、部門工資總和、最高工資和最低工資。
            select D.deptno 部門編號, D.dname 部門名稱, D.loc 部門地址, count(E.empno) 部門人數, round(avg(nvl(sal,0))) 平均工資, sum(nvl(sal,0)) 工資總和, max(nvl(sal,0)) 最高工資, min(nvl(sal,0)) 最低工資 from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno,D.dname,D.loc;
        分析:
            既然如今要統計每一個部門的信息,那麼必定要包含:部門編號、部門名稱、部門位置,部門人數、平均工資、總工資、最高工資、最低工資。
            如今確定不能只依靠一張表來完成,由於部門信息必定是dept表,而僱員的信息記錄在emp表。

            肯定所須要的數據表:
                dept表:部門編號,部門名稱,部門位置。
                emp表:統計出平均工資,總工資,最高工資,最低工資,和部門人數。
            肯定已知的關聯字段:
                僱員和部門:emp.deptno = dept.deptno;

            步驟一:將題目的要求轉換,將emp表和dept表關聯,查詢出部門編號,部門名稱,部門位置,僱員編號和姓名。
                    select D.deptno, D.dname, D.loc, E.empno, E.ename from emp E, dept D where D.deptno = E.deptno
                能夠發現deptno, dname, loc三個字段出現了重複數據,既然列上的信息重複,就必定具有了分組的條件。
            步驟二:使用多字段分組,同時實現統計信息。
                    select D.deptno, D.dname, D.loc, count(E.empno) count, round(avg(E.sal),2) avg, sum(E.sal) sum, max(E.sal) max,  min(E.sal) min from emp E, dept D where E.deptno = D.deptno group by D.deptno, D.dname, D.loc;
            步驟三:部門一共有四個部門,而且如今要求列出全部部門的統計信息,因此必須使用外鏈接。數據庫

                    select D.deptno, D.dname, D.loc, count(E.empno) count, round(avg(E.sal),2) avg, sum(E.sal) sum, max(E.sal) max, min(E.sal) min from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno, D.dname, D.loc; 

            步驟四:由於40部門沒有僱員信息,因此某些結果就是null,使用nvl函數進行處理。
                    select D.deptno, D.dname, D.loc, count(E.empno) count, round(avg(nvl(E.sal,0)),2) avg, sum(nvl(E.sal,0)) sum, max(nvl(E.sal,0)) max, min(nvl(E.sal,0)) min from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno, D.dname, D.loc;

    3.2 小結
        多字段分組是group by定義多個字段,而且多個字段必須同時重複。 

四、having子句
    在以前學習了關於查詢組成的相關子句,而having子句是這些子句之中的最後一個,與where很是類似。
    要求:
            掌握having子句的使用。
            理解having子句和where子句的區別。

    使用group by子句能夠實現數據的分組顯示,可是在不少時候每每須要對分組以後的數據進行再次的過濾,例如:要求選出部門人數超過5我的的部門信息,這樣的操做確定是先要經過部門進行分組統計,而後再使用統計結果進行數據的過濾,而要想實現這樣的功能就只能經過having子句來完成。
    語法:
        select [distinct] 分組字段1 [分組字段2..] from 表1 where 條件(s) group by 分組字段1 [,分組字段2] having 過濾條件(s)
    注意:
            having子句必須和group by子句一塊兒使用。
            having子句和group by子句先後順序不重要,由於有子句執行順序。
    全部子句的執行順序:
            from --> where --> group by --> having --> select --> order by;

    4.1 分組統計——範例
        查詢出全部平均工資大於2000的職位信息、平均工資、僱傭人數。
            select job,round(avg(sal)),count(empno) from emp -- where avg(sal) > 2000 group by job having avg(sal) > 2000
        須要注意的是:where子句中不能包含聚合函數。
        分析:
            首先不關心平均工資是否大於多少,只按照職位分組後進行統計。
                select job, round(avg(sal)),count(empno) from emp group by job; 
            如今但願能夠對分組後的數據進行過濾,若是如今使用where是不可能的,由於where在group by子句以前,而且where子句中不容許使用聚合函數。

    4.2 分組統計——範例
        列出至少有一個員工的全部部門編號、名稱,並統計出這些部門的平均工資、最低工資、和最高工資。
            select D.deptno, D.dname,round(avg(sal)),min(sal),max(sal) from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno,D.dname having count(*) > 1
        分析:
            肯定所須要的數據表:
                emp表:部門編號、名稱。
                dept表:統計信息。
            肯定已知的關聯字段:
                僱員與部門關聯:emp.empno = dept.deptno;
            步驟一:將emp表與dept表關聯查詢。
                select D.deptno, D.dname, E.empno, E.sal from emp E, dept D where E.deptno = D.deptno
            步驟二:如今部門名稱和部門編號出現了重複,能夠經過這兩個字段進行分組查詢。
                select D.deptno, D.dname, round(E.sal), min(E.sal), max(E.sal) from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno, D.dname;
            步驟三:須要針對分組後的結果進行篩選(人數大於1)
                select D.deptno, D.dname, round(E.sal), min(E.sal), max(E.sal) from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno, D.dname having count(E.empno) > 1;
                注意:having在select子句以前執行,因此having子句中不能使用select別名。 

    having和where的區別:
        where:在分組以前使用,不容許使用聚合函數。
        having:在分組以後使用(必須結合group by),容許使用聚合函數。

    4.3 分組統計——範例
        顯示非銷售人員工做名稱以及從事同一工做僱員的月工資的總和,而且要知足從事同一工做的僱員的月工資合計大於5000,輸出結果按月工資的總和升序排序。
            select job, sum(sal) sum from emp where job != 'SALESMAN' group by job having sum(sal) > 5000 order by sum;
         分析:
            步驟一:顯示全部非銷售人員的工做名稱。
                select distinct job from emp where job != 'SALESMAN'; 
            步驟二:發現須要對數據進行統計,統計月工資總和,使用sum函數。
                select job, sum(sal) sum from emp where job != 'SALESMAN' group by job 
            步驟三:針對於分組以後的工資總和進行篩選。
                select job, sum(sal) sum from emp where job != 'SALESMAN' group by job having sum(sal) > 5000
            步驟四:按照月工資進行升序排序,而order by子句是最後執行的,因此order by子句能夠使用select別名。
                select job, sum(sal) sum from emp where job != 'SALESMAN' group by job having sum(sal) > 5000 order by sum;

    4.4 小結
        having子句是在分組以後進行使用的,主要是爲了針對分組的結果進行過濾。 


8、子查詢
    首先對於SQL查詢語言而言,已經學習了完整的操做語法,而子查詢之中所講解的語法格式並無任何的新技術,或者說子查詢感受上跟Java的內部類是很類似的。 

    子查詢就是指在一個完整的查詢語句之中,嵌套若干個不一樣功能的小查詢,從而一塊兒完成一個複雜查詢的一種編寫形式。

    複雜查詢 = 限定查詢 + 多表查詢 + 統計查詢 + 子查詢。
    複雜查詢也是在筆試之中出現較多的部分。

一、簡單子查詢
    1.1 子查詢——範例
        查詢公司之中工資最低的僱員的完整信息。
            select * from emp where sal = (select min(sal) from emp)
        分析:
            步驟一:
                公司必定會存在最低工資,若是想要知道最低,須要使用min()函數。 
                    select min(sal) from emp;
            如今至關於知道了最低工資是800這個數據,若是說如今換一個思路,要求查詢出工資是800的僱員,此類查詢只須要在where子句之中設置一個條件便可,利用sal字段進行判斷。
            步驟二:由於此時不可能預先知道最低工資就能夠把上面的800換爲以前的min(sal)查詢。
                select * from emp where sal = (select min(sal) from emp;

        子查詢就是在一個完整的查詢之中定義了若干個小的查詢所造成的的複雜查詢,在編寫子查詢的時候必定要使用( )標記。

    1.2 子查詢返回的結果
        子查詢能夠返回的數據類型一共分爲四種:
            單行單例:返回的是一個具體列的內容,能夠理解爲一個單值數據。
            單行多列:返回一行數據中多個列的內容。
            多行單列:返回多行記錄中同一列的內容,至關於給出了一個操做範圍,能夠使用in。
            多行多列:查詢返回的結果是一張臨時表。

    1.3 子查詢語法:
        1)select中
            select (select 字段1,字段2..from 表 where 條件(s)) from 表 where 條件(s)
        2)from中
            select 字段 from (select 字段1,字段2 from 表)
        3)where中
            select 字段1, 字段2 from 表 where 條件(s) (select 字段1, 字段2... from 表);
        4)having中
            select 分組字段1, 分組字段2... from 表 where 條件(s) group by 分組字段1, 分組字段2... having 條件(s) (select 字段1, 字段2.. from 表);

    1.4 子查詢的常見操做
        where子句:此時自查詢返回的結果通常都是單行單列、單行多列、多行單列。
        having子句:此時子查詢返回的結果都是單行單列數據,爲了使用統計函數操做。
        from子句:此時子查詢返回的結果通常都是多行多類,能夠按照一張臨時表的形式進行操做。

        雖然子查詢以where、having、from爲主,可是select中也能夠出現。

    1.5小結
        一個查詢語句內部能夠定義多個子查詢。
        子查詢通常在where、from、having子句中出現較多。

二、where子查詢
    要求:
        能夠在where子句中處理單行單列子查詢、多行單列子查詢、單行多列子查詢。

    2.1 子查詢返回單行單列數據
        若是子查詢返回的是單行單列的數據,那麼子查詢所獲得的就是一個數值。
        查詢出基本工資比ALLEN低的所有僱員信息。
            select * from emp where sal < ( select sal from emp where ename = 'ALLEN');
        分析:
            步驟一:首先找到ALLEN的工資
                select sal from emp where ename = 'ALLEN';
                此時返回的是單行單列的數值。
            步驟二:在where子句中使用子查詢
                select * from emp where sal < (select sal from emp where ename = 'ALLEN');

    2.2 子查詢——範例
        查詢基本工資高於公司平均薪水的所有僱員信息。
            select * from emp where sal > (select avg(sal) from emp)
        分析:
            步驟一:須要知道公司的平均薪水,使用avg()函數。
                select avg(sal) from emp;
            步驟二:須要知道那些僱員的工資高於以前的查詢結果(平均工資)
                select * from emp where sal > (selest avg(sal) from emp);

    2.3 子查詢——範例
        查找出於ALLEN從事同一工做,而且基本工資高於僱員編號爲7521的所有僱員信息。
            select * from emp where job = (select job from emp  where ename = 'ALLEN') and sal > (select sal from emp where empno = 7521);
        分析:
            步驟一:找到ALLEN的工做
                select job from emp where ename = 'ALLEN';
            步驟二:找到7521的工資
                select sal from emp where empno = 7521; 
            步驟三:判斷職位和工做。
                select * from emp where job = (select job from emp where ename = 'ALLEN') and sal > (select sal from emp where empno = 7521);

    單行單列子查詢返回的就是一個數值。 express


    2.4 子查詢返回單行多列數據
        查詢與SCOTT從事同一工做而且工資相同的僱員信息。
            select * from emp where (job,sal) = (select job,sal from emp where ename = 'SCOTT') and ename != 'SCOTT';
        須要注意的是,比較字段順序不能顛倒。
        分析:
            步驟一:首先要找到SCOTT的工做以及工資。
                select job, sal from enp where ename = 'SCOTT';
            步驟二:既然返回的是單行兩列的數據,那麼就能夠在where中使用這兩個字段。
                select * from emp where (job,sal) = (select job, sal from emp where ename = 'SCOTT');
            步驟三:清楚SCOTT,只須要增長一個過濾條件便可。
                select * from emp where (job,sal) = (select job, sal from emp where ename = 'SCOTT') and ename != 'SCOTT';

    2.5 子查詢——範例
        查詢與僱員7566從事同一工做而且領導相同的所有僱員信息。
            select * from emp where (job,mgr) = (select job,mgr from emp where empno = 7566) and empno != 7566;
        分析:
            步驟一:找到7566的工做和領導 。
                select job, mgr from emp where empno = 7566; 
            步驟二:繼續判斷信息。
                select * from emp where (job, mgr) = (select job, mgr from emo where empno = 7566) and empno != 7566;

    2.3 子查詢——範例
        查詢與ALLEN從事同一工做而且僱傭時間爲同一年的所有僱員信息。
            select * from emp where (job,extract(year from hiredate)) = (select job, extract(year from hiredate) from emp where ename = 'ALLEN') and ename != 'ALLEN';
        分析:
            步驟一:查詢ALLEN的工做和僱傭日期。
                select job, hiredate from emp where ename = 'ALLEN'; 
            步驟二:繼續判斷僱傭年分和工做。
                select * from emp where (job, extract(year from hiredate) = (select job, extract(year from hiredate) from emp where ename = 'ALLEN') and ename != 'ALLEN';

    2.4 子查詢返回多行單列的數據。
        在使用多行子查詢時,主要使用三種操做符:in  any  all。

        in操做符:
            在以前所學習過的限定查詢之中已經使用過in操做符了,主要是設置範圍,也就是說若是子查詢返回的是多行單列數據,就至關於定義了數據的查詢範圍,既然是範圍的查詢,就須要使用in或not in操做符了。

    2.5 子查詢——範例
        查詢出每一個部門中工資最低的所有僱員信息。
            select * from emp where sal in (select min(sal) from emp group by deptno);
        分析:
            步驟一:按照部門分組,統計出每一個部門的最低工資。
                select min(sal) from emp group by deptno; 
            步驟二:使用in進行範圍查詢。
                select * from emp where sal in (select min(sal) from emp group by deptno);

    2.6 子查詢——範例
        查詢出與每一個部門中工資不是最低的所有僱員信息。
            select * from emp where sal not in (select min(sal) from emp group by deptno);
        分析:
            至關於對上一個結果進行求反操做。 

        既然說到了in以及not in的操做,那麼就有一點須要注意,關於null的問題,若是在in之中自查詢返回的數據有null,那麼不會影響查詢,可是若是在not in之中自查詢返回的數據包含null,那麼就不會返回任何的數據。 
            select * from emp where mgr not in (select mgr from emp);
        not in表示不在此範圍中,因此由於mgr包含null,因此不會返回任何數據。 

    2.7 any操做符
        any操做符有以下三種使用形式:
            = any:表示與子查詢中的每一個元素進行比較,功能與in相似。(等於任何一個)(<> any 不等價與not in)
            > any:比子查詢中返回結果的最小值要大。(大於任意一個)(包含了>= any)
            < any:比子查詢中返回結果的最大值要小。(小於任意一個)(包含了<= any)

    2.8 子查詢——範例
        查詢每一個部門經理的最低工資(假設每一個部門有多個經理)
        使用= any操做符完成查詢。
            select * from emp where sal = any (select min(sal) from emp where job = 'MANAGER' group by deptno);
        = any表示匹配其中任意一個值。
        若是此時使用了<> any,則表示表中的數據所有返回了,無心義。

    2.9 子查詢——範例
        使用>any操做符完成查詢,>any表示比子查詢的最小值要大。 
            select * from emp where sal > any(select min(sal) from emp where job = 'MANAGER' group by deptno);
 
    2.10 子查詢——範例
        使用<any操做符完成查詢,<any表示比子查詢的最大值要小。
             select * from emp where sal <any (select min(sal) from emp where job = 'MANAGER' group by deptno);

    2.11 some操做符
        在Oracle中(SQL之中),some和any的功能是相同的,這是後來添加的。 

    2.12 all操做符
        all操做符有如下三種用法:
            <>all:等價於not in。(所有不等於)(=all並不等價於in)。
            >all:比子查詢中最大的值還要大。(大於所有)
            <all:比子查詢中最小的值還要小。(小於所有)

    2.13 子查詢——範例
        使用<>all操做符完成查詢。
        查詢每一個部門中的經理工資不是最低工資的僱員信息。
            select * from emp where sal != all(select min(sal) from emp where job = 'MANAGER' group by deptno);
        這個功能和not in是徹底同樣的,但是若是使用的是=all,是沒有數據返回的。

    2.14 子查詢——範例
        使用>all操做符完成查詢(大於所有子查詢返回的數據) 
            select * from emp where sal > all(select min(sal) from emp where job = 'MANAGER' group by deptno);

    2.15 子查詢——範例
        使用<all操做符完成查詢(小於所有子查詢返回的數據)
            select * from emp where sal < all(select min(sal) from emp where job = 'MANAGER' group by deptno);

    總結:
        對於in操做符,瞭解in和not in就能夠了。
        any操做符:
            *   =any:功能與in相同
            *   >any:表示比子查詢中返回的最小值要大。
            *   <any:表示比子查詢中返回的最大值要小。
        all操做符:
            *   !=all:功能與not in相同
            *   >all:比子查詢中返回的最大值要大。
            *   <all:比子查詢中返回的最小值要小。

    2.16 空數據判斷
        在SQL之中提供了一個exists結構用於判斷子查詢是否有數據返回,若是子查詢中有數據返回,則exists結構返回true,反之返回false。

        結構:
            select * from emp where exists(select * from emp where empno = 9999);
        此時因爲不存在9999編號的僱員,因此在這個地方exists()判斷返回的就是false,就不會有所有的結果返回了。
        若是如今有結果返回,也就是說子查詢有內容,exists結構知足,數據就會返回。

    2.17 子查詢——範例
        使用not exists。
            select * from emp where not exists(select * from emp where empno = 9999);
        對exists求反。

    2.18 小結
        where子句能夠判斷單個數值、多個數值。
        使用in、any、all能夠處理多行單列的子查詢。
        利用exists()能夠判斷查詢結果是否爲null。

三、having子句中的子查詢
    having子句必定是結合group by子句一塊兒使用的,其主要的目的是進行分組後數據的再次過濾,與where子句不一樣的是,having子句在分組後能夠使用統計函數,而where是在分組前,不能使用統計函數。

    要求:
        掌握having子句使用子查詢的形式。

    通常而言在having子句中出現子查詢,子查詢返回的數據每每是單行單列的,它是按照一個數值的方式返回,再經過統計函數進行過濾。也就是說,由於having子句中能夠使用統計函數,而統計函數中須要一個參數,因此子查詢返回的單行單列的數值能夠當作統計函數的參數使用。

    3.1 子查詢——範例
        查詢部門編號、僱員人數、平均工資,並要求這些部門的平均工資高於公司平均薪資。
            select deptno,count(empno),round(avg(sal)) from emp group by deptno having round(avg(sal)) > (select round(avg(sal)) from emp);
        分析:
            步驟一:查詢出公司的平均薪水。
                select round(avg(sal)) from emp;
            步驟二:按照deptno字段進行分組,而且統計部門的信息。
                select deptno, count(empno), avg(sal) from emp group by deptno;
            步驟三:使用having子句執行分組後的數據過濾,因此須要在having子句中執行子查詢。
                select deptno, count(empno), round(avg(sal)), from emp group by deptno having avg(sal) > (select avg(sal) from emp);

    3.2 子查詢——範例
        查詢出每一個部門平均工資最高的部門名稱及平均工資。
            select D.dname,round(avg(sal)) from emp E, dept D where E.deptno = D.deptno group by D.dname having avg(sal) = (select max(avg(sal)) from emp group by deptno);
        分析:
            若是是最高的平均工資,那麼確定使用統計函數的嵌套來完成,在統計函數嵌套使用的時候,select子句中是不容許出現任何的字段,包括分組字段。
            步驟一:求出平均工資最高的工資。
                select max(avg(sal)) from emp;
            步驟二:如今已經知道了最高的平均工資,可是須要知道部門名稱,因此須要使用部門表。

    3.3 小結
        在having子句中使用子查詢,子查詢返回的都是單行單列數據,同時也能夠在having中利用統計函數進行判斷。

四、在from子句之中使用子查詢
    from子句的主要功能是肯定數據的來源,那麼來源都是數據表,表的特徵是:行 + 列的集合,只要是在from子句之中出現的內容通常都是多行多列的子查詢返回的內容,
    要求:
        掌握from子句中子查詢的使用。

    4.1 子查詢——範例
        要求查詢出每一個部門的編號、名稱、位置、部門人數、平均工資。

            在以前學習分組統計的時候學習過多字段分組操做,當時的基本實現以下:
                select D.deptno, D.dname, D.loc, count(E.empno), round(avg(E.sal)) from emp E, dept D where E.deptno(+) = D.deptno group by D.deptno,D.dname, D.loc;
            可是除了以上作法以外,如今還有另一種作法,那就是使用子查詢完成。

        分析:
            步驟一:查詢出每一個部門的基本信息,只須要查詢dept表便可。
                select * from dept;
            步驟二:統計信息,按照部門編號分組統計。
                select deptno, count(empno), avg(sal) from emp group by deptno;
                注意:在子查詢中,統計函數必須使用別名。
                    select deptno dno, count(empno) count, round(avg(sal)) avgsal from emp group by deptno;
            步驟三:
                第一個步驟返回的是dept的信息,第二個步驟是部門的統計結果,如今能夠發現,將第一個步驟的結果和第二個步驟的結果相結合就能夠獲得最終結果,而這個時候的結合必定是屬於表的結合,只不過一個是dept實體表,另外一個是統計的查詢結果,是一個臨時表。
                select D.deptno, D.dname, D.loc, temp.count, temp.avg from dept D, (select deptno dno, count(empno) count, cound(avg(sal)) avg from emp group by deptno) temp where D.deptno = temp.dno(+);

        問題:兩種操做均可以實現一樣的效果,那麼使用哪一種操做呢? 
            爲了解決此問題,能夠將數據擴大一百倍,即emp表中的數據爲1400條記錄,dept表中的數據爲400條記錄。
            實現一:多字段分組。
                當dept和emp表關聯的時候必定會存在笛卡爾積,此時數據量爲1400*400 = 560000條記錄。
            實現二:子查詢。
                *   統計:emp表的1400條記錄,並且最終的統計結果的行數不可能超過400行(由於dept表只有400條)。
                *   多表關聯:dept表的400行記錄 * 子查詢的400條記錄 = 160000條記錄。
                *   最終結果:160000 + 1400 = 161400條記錄。
            使用子查詢其實是解決多表查詢所帶來的性能問題,因此在開發之中必定會大量使用子查詢。

    4.2 子查詢——範例
        查詢出全部在部門「SALES」工做的員工的編號、姓名、基本工資、獎金、職位、僱用日期、部門的最高和最低工資。
            select empno, ename, sal, comm, job, hiredate, temp.max, temp.min from emp E, (select deptno dno, max(sal) max, min(sal)  min from emp group by deptno) temp where E.deptno = (select deptno from dept where dname = 'SALES')
and E.deptno = temp.dno;
        子查詢負責統計信息,使用temp表表示臨時表的統計結果。
        分析:
            肯定所須要的數據表:
                dept表:銷售部(SALES),最終是根據銷售部來統計的,因此須要知道銷售部的部門編號。
                emp表:編號、姓名、職位、僱用日期、基本工資、獎金。
                emp表:統計最高和最低工資。
            步驟一:查詢出銷售部的部門編號。
                select deptno from dept where dname = 'SALES';
            步驟二:找到在此部門的所有僱員信息。
                select empno, ename, sal, comm, job, hiredate from emp where deptno = (select deptno from dept where dname = 'SALES');
            步驟三:若是要統計出最高和最低工資,使用的必定是max()和min()函數,可是對於統計函數的使用限制:
                *   統計函數要麼單獨使用,要麼結合group by使用,單獨使用的時候select子句中不容許出現其餘字段。
                *   結合group by子句使用的時候select子句中容許出現分組字段。
                *   統計函數嵌套的時候不容許出現任何字段。
                發如今整個select查詢中須要使用統計函數,可是卻沒法直接使用統計查詢,那麼就能夠在子查詢中完成,並且子查詢必定返回多行多列的數據,也就是在from子句中出現。 
                select empno, ename, sal, comm, job, hiredate, temp.max, temp.min from emp E, (select deptno dno, max(sal) max, min(sal) min from emp group by deptno) temp where deptno = (select deptno from dept where dname = 'SALES') and E.deptno = temp.dno;

    4.3 子查詢——範例
        查詢出全部薪資高於公司平均薪資的員工編號、姓名、基本工資、職位、僱傭日期、所在部門名稱、位置、上級領導姓名、公司的工資等級、部門人數、部門平均工資、部門平均服務年限。
            select E1.empno, E1.ename, E1.sal, E1.job, E1.hiredate, D1.dname, D1.loc, E2.ename 領導, S1.grade, E2.count, E2.avg1, E2.avg2 from emp E1, dept D1, emp E2, salgrade S1, (select deptno dno, count(empno) count, round(avg(sal)) avg1, round(avg(months_between(sysdate,hiredate)/12)) avg2  from emp group by deptno) E2 where E1.sal > (select avg(sal) from emp) and E1.deptno = D1.deptno and E1.mgr = E2.empno(+) and E1.sal between S1.losal and S1.hisal and E1.deptno = E2.dno;
        分析:
            肯定所須要的數據表:
                emp表:員工編號、姓名、基本工資、職位、僱傭日期。
                dept表:部門名稱、位置。
                emp表:上級領導姓名。
                salgrade表:工資等級。
                emp表:統計出部門人數、平均工資、平均服務年限。
            肯定已知的關聯字段:
                僱員和部門:emp.deptno = dept.deptno;
                僱員和領導:emp.mgr = emp.deptno;
                僱員和工資等級:emp.sal between slagrade.losal and salgrade.hisal;
            步驟一:使用avg()函數統計公司的平均薪資。
                select avg(sal) from emp; 
                結果返回單行單列的數值,必定是在where或者having中出現,可是根據分析,只能在where中出現。
            步驟二:查詢出高於此平均工資的員工編號、姓名、基本工資、職位、僱用日期。
                select E1.empno, E1.ename, E1.sal, E1.job, E1.hiredate from emp E1 where E1.sal > (select avg(sal) from emp);
            步驟三:須要知道部門的名稱、位置,因此增長dept表
                select E1.empno, E1.ename, E1.sal, E1.job, E1.hiredate, D1.dname, D1.loc from emp E1, dept D1 where E1.sal > (select avg(sal) from emp) and E1.deptno = D1.deptno;
            步驟四:須要找到領導的姓名,必定是自身關聯,必定要想到KING的問題,由於KING沒有領導,mgr爲null,須要使用外鏈接。
                select E1.empno, E1.ename, E1.sal, E1.job,E1.hiredate, D1.dname, D1.loc, E2.ename from emp E1, dept D1 where E1.sal >(select avg(sal) from emp) and E1.deptno = D1.deptno and E1.mgr = E2.empno(+);
            步驟五:查詢工資等級。
                select E1.empno, E1.ename, E1.sal, E1.job,E1.hiredate, D1.dname, D1.loc, E2.ename, S1.grade from emp E1, dept D1, salgrade S1 where E1.sal >(select avg(sal) from emp) and E1.deptno = D1.deptno and E1.mgr = E2.empno(+) and E1.sal between S1.losal and S1.hisal;
            步驟六:加入統計結果。
                見查詢。

    4.4 子查詢——範例
        查詢出薪資比「ALLEN」或「CLARK」高的全部員工的編號、姓名、基本工資、部門名稱、領導姓名和部門人數。
            select E1.ename, E1.ename, E1.sal, D1.dname, E2.ename, temp.count from emp E1, dept D1, emp E2, (select deptno dno, count(empno) count from emp group by deptno) temp where E1.sal > any (select sal from emp where ename in ('ALLEN', 'CLARK'))
and E1.deptno = D1.deptno and E1.mgr = E2.empno(+) and temp.dno = D1.deptno and E1.ename not in ('ALLEN', 'CLARK');
        分析:
            肯定所須要的數據表:
                emp表:員工編號、姓名、基本工資。
                dept表:部門名稱。
                emp表:領導姓名。
                emp表:部門人數統計。
            肯定已知的關聯字段:
                部門和僱員:emp.deptno = dept.deptno;
                僱員和領導:emp.mgr = memp.empno(+);
            步驟一:找到ALLEN或CLARK的工資。
                由於存在或操做,因此能夠使用any操做符。 
                    select sal from emp where ename in ('ALLEN', 'CLARK');
                此時返回的是多行單列數據,那麼多行單列的判斷只可以使用in、any或all進行操做,因此根據分析應該使用any進行操做,由於只要比其中一個大便可。
             步驟二:查詢僱員信息。
                select E1.empno, E1.ename, E1.sal from emp E1 where E1.sal > any (select sal from emp where ename in ('ALLEN', 'CLARK')) and E1.ename not in ('ALLEN', 'CLARK');
             步驟三:加入部門表,列出部門名稱。
                 select E1.empno, E1.ename, E1.sal, D1.dname from emp E1, dept D1 where E1.sal > any (select sal from emp where ename in ('ALLEN', 'CLARK')) and E1.ename not in ('ALLEN', 'CLARK') and E1.deptno = D1.deptno;
             步驟四:使用emp表進行自身關聯,查詢出領導姓名。
                select E1.empno, E1.ename, E1.sal, D1.dname, E2.ename from emp E1, dept D1, emp E2 where E1.sal > any (select sal from emp where ename in ('ALLEN', 'CLARK')) and E1.ename not in ('ALLEN', 'CLARK') and E1.deptno = D1.deptno and E1.mgr = E2.empno(+);
             步驟五:此時的select子句中不可能直接使用統計查詢了,只可以使用from子句進行子查詢來完成。
                select E1.empno, E1.ename, E1.sal, D1.dname, E2.ename, temp.count from emp E1, dept D1, emp E2, (select deptno dno, count(empno) count from emp group by deptno) temp where E1.sal > any (select sal from emp where ename in ('ALLEN', 'CLARK')) and E1.ename not in ('ALLEN', 'CLARK') and E1.deptno = D1.deptno and E1.mgr = E2.empno(+) and E1.deptno = temp.dno;

    4.5  子查詢——範例
         列出公司各個部門的經理的姓名、薪資、部門名稱、部門人數、部門平均工資。
            select E.ename, E.sal, temp.count, D.dname, temp.avg from emp E, (select deptno dno, count(empno) count, round(avg(sal),2) avg from emp group by deptno) temp, (select deptno dno, dname dname from dept) D where job = 'MANAGER' and E.deptno = temp.dno and E.deptno = D.dno;

        分析:
            肯定所須要的數據表:
                emp表:經理的姓名、薪資。
                dept表:部門名稱。
                emp表:部門人數、部門的平均工資。
            肯定已知的關聯字段:
                僱員(經理):emp.deptno = dept.deptno;
            步驟一:找到經理的姓名和薪資。
                select ename, sal from emp where job = 'MANAGER';
            步驟二:找到部門名稱。
                select ename, sal, D1.dname from emp E1, dept D1 where E1.job = 'MANAGER' and E1.deptno = D1.deptno;
            步驟三:須要統計結果,此時select子句中沒法直接使用統計查詢,那麼就使用子查詢。
                select ename, sal, D1.dname, temp.count, temp.avg from emp E1, dept D1, (select deptno dno, count(empno) count, round(avg(sal)) avg from emp group by deptno) temp where E1.job = 'MANAGER' and E1.deptno = D1.deptno and temp.dno = E1.deptno;

    4.6 小結
        from子句出現的子查詢返回結果爲多行多列。
        利用子查詢能夠解決多表查詢所帶來的性能問題。

五、select子句中的子查詢
    要求:瞭解select子句使用子查詢操做的代碼形式。

    子查詢能夠出如今任意位置上,不過從實際的項目來說,在where、from、having子句中使用子查詢的狀況仍是比較多的,而對於select子句,只是以一種介紹的形式進行說明。

    5.1 子查詢——範例
        查詢出公司每一個部門的編號、名稱、位置、部門人數、平均工資。
            select D.deptno, D.dname, D.loc, (select count(empno) from emp where deptno = D.deptno) count, (select avg(empno) from emp where deptno = D.deptno) avg from dept D;
        這類子查詢徹底能夠經過其餘的形式來實現,因此意義並不大。
    5.2 小結
        select子句中出現子查詢的狀況通常比較少見,瞭解便可。

六、with子句
    要求:能夠使用with子句建立臨時查詢表。

    臨時表實際上就是一個查詢結果,若是一個查詢結果返回的是多行多列,那麼就能夠將其定義在from子句以後,表示爲一張臨時表。可是除了在from子句之中出現臨時表以外,也能夠利用with子句直接定義臨時表,也就是繞開了from子句。

    6.1 子查詢——範例
        使用with子句將emp表中的數據定義爲臨時表。
            with E as (select * from emp) select * from E;
        E就表示整個查詢結果

    6.2 子查詢——範例
        查詢每一個部門的編號、名稱、位置、部門平均工資、人數。
            with E as (select deptno dno, round(avg(sal)) avg, count(empno) count from emp group by deptno)編程

select D.deptno, D.dname, D.loc,  E.avg, E.count from dept D, E where D.deptno = E.dno(+);


    6.3 子查詢——範例
        查詢每一個部門工資最高的僱員編號、姓名、職位、僱傭日期、工資、部門編號、部門名稱、顯示的結果按照部門編號降序排序。
            with temp as (select deptno dno, max(sal) max from emp group by deptno) select E1.deptno, E1.empno, E1.ename, E1.job, E1.hiredate, E1.sal, D.dname from emp E1, temp, dept D where E1.sal = temp.max and E1.deptno = D.deptno forder by E1.deptno desc;
        分析:
            步驟一:使用with定義臨時表,統計每一個部門的最高工資。

    6.4 小結
        with子句能夠建立一個臨時表供查詢使用。

七、分析函數
    要求:
        理解分析函數的主要語法;
        理解分窗操做的使用;
        瞭解基本分析函數。

    傳統SQL問題:
        雖然利用SQL之中提供的各類查詢命令能夠完成大部分的查詢需求,可是還有許多功能是沒法實現的,例如:
            計算運行總量:逐一累加當前行與其以前行的每行記錄數據。
            查找當前行數據佔總數據的百分比。
            分區顯示:按照不一樣的部門或職位進行排列、統計。
            計算流動數據行的平均值。

    傳統SQL就是SQL標準規定的語法:select、from、where、group by、having、order by,可是傳統SQL所可以完成的功能實際上並很少。
    在分析函數之中也能夠使用若干統計函數count()等進行操做。

    7.1 分析函數基本語法
        基本語法:
            函數名稱([參數,...]) over (
                    partition by子句 字段, ...
                    [order by 子句 字段, ... [asc | desc] [nulls first | nulls last]]
                    [windowing子句]
                );

        本語法組成以下:
            函數名稱:相似於統計函數(count() sum()等),可是在此時有了更多的函數支持。
            over子句:爲分析函數指明一個查詢結果集,此語句在select子句之中使用。
            partition by子句:將一個簡單的結果集分爲N組(或稱爲分區),而後按照不一樣的組對數據進行統計。
            order by子句:明確指明數據在每一個組內的排列順序,分析函數的結果與排列順序有關;
                    nulls first | nulls last:表示返回數據行中包含null值是出如今排序序列前仍是序列尾。
            windowing子句(代名詞):給出在定義變化的固定的數據窗口方法,分析函數將對此數據進行操做。

        組合順序:
            在分析函數之中存在三種子句:partition by、order by、windowing,這三種子句的組合順序有以下幾種:
            第一種組合:
                函數名稱([參數, ...]) over (partition by子句, order by子句  windowing子句);
            第二種組合:
                函數名稱([參數, ...]) over (partition by子句  order by子句);
            第三種組合:
                函數名稱([參數, ...]) over (partition by子句);
            第四種組合:
                函數名稱([參數, ...]) over (order by子句  windowing子句);
            第五種組合:
                函數名稱([參數, ...]) over (order by子句);
            第六種組合:
                函數名稱([參數, ...]) over();

    7.2 分析函數——範例
        使用partition by子句。
        傳統問題:
            select deptno, ename, sal from emp;
            這只是一個簡單查詢,可是在這個select子句中,是不可能出現統計函數的,由於統計函數於要麼單獨使用,要麼結合group by使
用。可是若是如今使用分析函數,那麼就不同了。
            select ename, sal, sum(sal) over (partition by deptno) from emp;
        如今的數據是按照部門的範疇進行了統計,每行數據以後都會有統計的結果存在。

        若是不進行分區操做:
            select ename, sal, sum(sal) over () from emp;
            若是沒有分區,則會把全部的數據當作一個區,一塊兒進行統計。

        使用partition by子句設置多個分區:
            select deptno, job, ename, sal, sum(sal) over(partition by deptno, job) sum from emp;

    7.3 分析函數——範例
        使用order by子句。
        order by子句的做用主要就是進行排序,如今實現的是分區內的數據排序,而這個排序會直接影響到最終的查詢結果。

        按照部門編號分區,而後按照工資進行降序排序:
            select deptno, ename, sal, rank() over(partition by deptno order by sal desc) from emp;

        設置多個排序字段:
            select deptno, ename, sal, hiredate, rank() over(partition by deptno order by sal, hiredate desc) from emp;

        直接利用order by排序全部數據:
            select deptno, ename, sal, hiredate, sum(sal) over(order by ename desc) from emp;
            如今若是不寫分區操做,那麼就表示爲全部數據進行排序。
            它如今是將全部的數據變爲一組,而後按照姓名進行排序操做。

        order by子句選項:
            在order by子句之中還存在兩個選項:nulls first和nulls last,其中nulls first表示在進行排序時,出現null值的數據行排列在最前面,而nulls last則表示出現的null值數據行排列在最後。
            select deptno, ename, sal, comm, rank() over(order by comm desc nulls last), sum(sal) over(order by comm nulls last) from emp;

    7.4 Windowing子句
        分窗子句主要是用於定義一個變化或固定的數據窗口方法,主要用於定義分析函數在操做行的集合,分窗子句有兩種實現方式:
            實現一:值域窗(range window),邏輯偏移,當前分區之中當前行的前N行到當前行的記錄集。
                10~15就是值域。 
            實現二:行窗(rows window),物理偏移,以排序的結果順序計算偏移當前行的起始行記錄集。
                3~5行是行域。 
        而若是要想指定range或rows的偏移量,則能夠採用以下的幾種排序列:
            range | rows 數字 preceding;
            range | rows between unbounded preceding and current row;
            range | rows between current row and unbounded following;
        以上的幾種排列之中包含的概念以下:
            preceding:主要是設置一個偏移量,這個偏移量能夠是用戶設置的數字,也能夠是其餘標記。
            between...and:設置一個偏移量的操做範圍。
            unbounded preceding:不限制偏移量的大小。
            following:若是不寫此語句則表示使用上N行與當前行指定數據進行比較,若是編寫此語句,表示當前行與下N行數據進行比較。

    7.5 分析函數——範例
        驗證range子句:
            range子句設置的是一個邏輯偏移量。
            select deptno, ename, sal, sum(sal) over(partition by deptno order by sal range 300 preceding) sum from emp;
            如今的結果是按照向上N行的記錄進行偏移,也能夠採用向下匹配方式進行處理。 

        設置偏移量爲300,採用向下匹配方式進行處理:
            select deptno, ename, sal, sum(sal) over (partition by deptno order by sal range between 0 preceding and 300  following) sum from emp;
        發現如今顯示結果仍是與後面的N行數據進行匹配。同時也能夠匹配當前行:
            select deptno, ename, sal, sum(sal) over (partition by deptno order by sal range between 0 preceding and current row) sum from emp;

        使用unbounded不設置邊界:
            select deptno, ename, sal, sum(sal) over(partition by deptno order by sal range between unbounded preceding and current row) sum from emp;
            如今的結果就是在一個區域內進行逐行的操做,並不設置偏移量。
            current row表示當前行。 

        若是是物理偏移,使用rows子句便可:
            設置2行的物理偏移量。
                select deptno, ename, sal, sum(sal) over(partition by deptno order by sal rows 2 preceding) sum from emp;
            如今就是按照部門編號分組,而後採用當前行與前兩行數據進行計算。

        設置查詢行範圍:
            select deptno, ename, sal, sum(sal) over(partition by deptno order by sal rows between unbounded preceding and unbounded following) sum from emp;
            此時與最先的按照部門分區,進行求和運算道理是相同的。

    7.6 數據統計函數
        sum([distinct | all] 表達式)        計算分區(分組)中的數據累加之和。
        min([distinct | all] 表達式)         查找分區(分組)中的最小值。
        max([distinct | all] 表達式)        查找分區(分組)中的最大值。
        avg([distinct | all] 表達式)         計算分區(分組)中的數據平均值。
        count([distinct | all] | * )             計算分區(分組)中的數據量。
        數據統計函數和以前的分組統計中所使用的函數區別不大。

    7.7 分析函數——範例
        查詢僱員編號是7369的僱員姓名、職位、基本工資、部門編號、部門的人數、平均工資、最高工資、最低工資和總工資。
        分析:
            第一反應確定是採用多表查詢來完成操做,可是如今有了分析函數,就能夠利用分區來完成。
            步驟一:統計出所有數據。
                select empno, ename, job, sal, deptno, count(empno) over (partition by deptno) count, round(avg(sal) over (partition by deptno)) avg, sum(sal) over (partition by deptno) sum, max(sal) over (partition by deptno) max, min(sal) over (partition by deptno) min from emp; 
                必須先求出所有的數據,不然加入where empno = 7369以後,部門人數就爲1,由於分析函數是根據現有數據進行操做的。
            步驟二:以上的結果返回的是多行多列,因此就是一張數據表的結構,能夠經過from子查詢來進行操做。
                select * from (select empno, ename, job, sal, deptno, count(empno) over (partition by deptno) count, round(avg(sal) over (partition by deptno)) avg, sum(sal) over (partition by deptno) sum, max(sal) over (partition by deptno) max, min(sal) over (partition by deptno) min from emp) temp where temp.empno = 7369;
        若是子查詢外部須要使用子查詢中的字段,例如:where empno = 7369,那麼在子查詢中必須出現該字段。 

    7.8 分析函數——範例
        查詢每一個僱員的編號、姓名、基本工資、所在部門的名稱、部門位置以及此部門的平均工資、最高和最低工資。
            select E.empno, E.ename, E.sal, D.dname, D.loc, count(empno) over (partition by E.deptno order by sal range between bnbounded preceding and unbounded following) count, round(avg(sal) over (partition by E.deptno order by sal range between unbounded preceding and unbounded following)) avg, max(sal) over (partition by E.deptno order by sal range between unbounded preceding and unbounded following) max, min(sal) over (partition by E.deptno order by sal range between unbounded preceding and unbounded following) min from emp E, dept D where E.deptno = D.deptno;
        分析:
            肯定所須要的數據表:
                dept表:部門編號、名稱、位置。
                emp表:僱員編號、姓名、工資、統計信息。
            肯定已知的關聯字段:
                僱員和部門:emp.deptno = dept.deptno;
            步驟一:進行多表關聯,查詢僱員的編號、姓名、基本工資、部門名稱和部門位置。
                select E.empno, E.ename, E.sal, D.dname, D.loc from emp E, dept D where E.deptno = D.deptno;
            步驟二:加入統計信息。

    7.9 等級函數
        rank()函數:
            根據order by子句的排序字段,從分區(分組)查詢的每一行數據,按照排序生成序號,大小相同時,會出現相同序號。
        dense_rank()函數:
            根據order by子句的排序字段,從分區(分組)查詢的每一行數據,按照排序生成序號,大小相同時,不會出現相同序號。
        first:
            取出dense_rank()函數返回集合中的第一行數據。
        last:
            取出dense_rank()函數返回集合中的最後一行數據。
        first_value(列)函數:
            返回分區(分組)中的第一個值。
        last_value(列)函數:
            返回分區(分組)中的最後一個值。
        lag(列名稱 [, 行數字] [, 默認值])函數:
            訪問分區(分組)中指定前N行的記錄,若是沒有則返回默認值。
        lead(列名稱 [, 行數字] [, 默認值])函數:
            訪問分區(分組)中指定後N行的記錄,若是沒有則返回默認值。
        row_number()函數:
            返回每組中的行號。

    7.10 分析函數——範例
        使用rank()函數和dense_rank()函數。
            select deptno, ename, sal,   rank() over (partition by deptno order by sal) ranl_result, dense_rank() over (partition by deptno order by sal) dense_rank_result from emp;設計模式

        使用rank()函數的時候若是有相同值,那麼會出現跳號狀況,若是是dense_rank()函數,則繼續保持序號順序。


    7.11 分析函數——範例
        使用row_number()函數。
                select deptno, ename, sal, row_number() over (partition by deptno order by sal) row_result_deptno, row_number() over order by sal) row_result_all from emp; 
        row_number()函數的功能就是用於生成行號。
        針對於全部的數據(row_result_all),row_number()函數會根據全部的數據自動生成順序行號,可是在每個分區中,也存在一類行號,該行號隨着排序的不一樣而分開,可是在每個分組中依然保持順序排序。

    7.12 keep語句
        keep語句的功能是保留知足條件的數據。
        keep語句必須結合group by使用。
        分組函數
            keep (dense_rank() first | last order by 表達式 [asc | desc] [nulls [first | last]], ...) [over () 分區查詢];
        查詢每一個部門的最高工資和最低工資。
            select deptno, max(sal) keep (dense_rank first order by sal desc) max_salary, min(sal) keep (dense_rank last order by sal desc) min_salary from emp group by deptno
        keep語句的功能是保留知足條件的數據,並且必須在使用dense_rank()函數肯定集合後才能夠使用,經過first或last取得集合中的數據。

    7.13 分析函數——範例
        驗證first_value()與last_value()函數。
            select deptno, empno, ename, sal, first_value(sal) over (partition by deptno order by sal range between unbounded preceding and unbounded following) first_result, last_value(sal) over (partition by deptno order by sal range between unbounded preceding and unbounded following) last_result from emp;
        over()是聲明一個數據集合,而first_value()或last_value()函數取得的是集合中的首行或尾行。

    7.14 分析函數——範例
        觀察lag()和lead()函數。
            select deptno empno, sal, lag(sal,2,0) over (partition by deptno order by sal) lag_result, lead(sal,2,0) over (partition by deptno order by sal) lead_result from emp安全


    7.15 報表函數
        cume_dist()函數:
            計算一行在分區(分組)中的相對位置。
       ntile(數字)函數:
           將一個分區(分組)分爲「表達式」的散列表示。
       ratio_to_report(表達式)函數:
           該函數計算expression/(sum(expression))的值,它給出相對於總數的百分比。
 
    7.16 cume_dist()函數
        計算相對位置。
        例如:假設分區有5條記錄,那麼這些記錄會按照:一、0.八、0.六、0.四、0.2的方式進行劃分。 

        select deptno, ename, sal, cume_dist() over (partition by deptno order by sal)cume from emp where deptno in(10,20);
 
    7.17 ntile()函數
        ntile()函數是針對數據分區中的有序結果集進行劃分。
             select deptno, sal, sum(sal) over(partition by deptno order by sal) sum_result, ntile(3) over(partition by deptno order by sal) ntile_result_a, ntile(6) over(partition by deptno order by sal)ntile_result_b from emp;

    7.18 ratio_to_report()函數
        ratio_to_report()函數是按照總體數據的百分比顯示。
            select deptno, sum(sal), round(ratio_to_report(sum(sal)) over(),3) * 100 ||'%' precent from emp group by deptno;

    7.19 小結
        使用分析函數能夠進行更爲複雜的查詢報表顯示。

八、行列轉換
    要求:
        瞭解行列轉換的基本概念。
        瞭解pivot和unpivot函數的使用。

    行列轉換嚴格來說是一種小技巧,爲了說明問題,下面首先經過一個程序演示。

    8.1 行列轉換——範例
        查詢每一個部門中各個職位的總工資,按照部門編號以及職位進行分組。
            select deptno,job, sum(sal) from emp group by deptno,job
        若是按照原始的方式實現,那麼只須要利用decode()函數就能夠了:
            select deptno, sum(decode(job, 'PRESIDENT', sal, 0)) PRESIDENT_job, sum(decode(job, 'MANAGER', sal, 0)) MANAGER_job, sum(decode(job, 'ANALYST', sal, 0)) ANALYST_job,  sum(decode(job, 'CLERK', sal, 0)) CLERK_job, sum(decode(job, 'SALESMAN', sal, 0)) SALESMAN_job from emp group by deptno;
        以上的方式是使用decode()函數,可是這個函數是屬於Oracle本身的特點函數,那麼若是沒有decode()函數呢?
        只能利用select子句的子查詢來完成:
            select deptno dno, (select sum(sal) from emp where job = 'PRESIDENT' and empno = E.empno) PRESIDENT_job, (select sum(sal) from emp where job = 'MANAGER' and empno = E.empno) MANAGER_job, (select sum(sal) from emp where job = 'ANALYST' and empno = E.empno) ANALYST_job, (select sum(sal) from emp where job = 'CLERK' and empno = E.empno) CLERK_job, (select sum(sal) from emp where job = 'SALESMAN' and empno = E.empno) SALESMAN_job from emp E;
        此時列出的是各個職位的統計信息,可是結果仍是存在差別,那麼則繼續嵌套子查詢:
            select temp.dno, sum(PRESIDENT_job), sum(MANAGER_job), sum(ANALYST_job), sum(CLERK_job), sum(SALESMAN_job)服務器

from (select deptno dno, (select sum(sal) from emp where job = 'PRESIDENT' and empno = E.empno) PRESIDENT_job, (select sum(sal) from emp where job = 'MANAGER' and empno = E.empno) MANAGER_job, (select sum(sal) from emp where job = 'ANALYST' and empno = E.empno) ANALYST_job, (select sum(sal) from emp where job = 'CLERK' and empno = E.empno) CLERK_job, (select sum(sal) from emp where job = 'SALESMAN' and empno = E.empno) SALESMAN_job from emp E) temp group by temp.dno order by temp.dno;

        雖然實現了功能,可是從感受上講,以爲仍是有些過於複雜了。

    8.2 pivot()函數
        在Oracle11g版本以後,專門增長了pivot和unpivot兩個轉換函數。
        pivot函數:
            select * | 列 from 子查詢 pivot( 統計函數(列) for 轉換列名稱 in (內容1 [[as]別名]), 內容2[[as]別名] ) where 條件....

        使用pivot函數進行行列轉換:
            select * from ( select deptno, job, sal from emp ) pivot( sum(sal) for job in ( 'PRESIDENT' as PRESIDENT_job, 'MANAGER' as MANAGER_job, 'ANALYST' as ANALYST_job, 'CLERK' as CLERK_job, 'SALESMAN' as SALESMAN_job ) ) order by deptno;
        發現使用pivot()函數操做起來實現的轉換更加容易理解,這個函數還能夠使用一個any變爲XML數據顯示。
            select * from (select deptno, job, sal from emp) pivot XML(sum(sal) for job in (any)) order by deptno;

    8.3 行列轉換——範例
        查詢更多統計信息,包括最高和最低工資,只須要利用分析函數便可。
            select * from (select job, deptno, sal, sum(sal) over (partition by deptno) sum_sal, max(sal) over (partition by deptno) max_sal, min(sal) over (partition by deptno) min_sal from emp ) pivot ( sum(sal) for job in( 'PRESIDENT' as PRESIDENT_job, 'MANAGER' as MANAGER_job, 'ANALYST' as ANALYST_job, 'CLERK' as CLERK_job, 'SALESMAN' as SALESMAN_job)) order by deptno;
        如今查詢的再也不是一個純粹的職位,以後還包含了其餘的統計信息,在以前所設置的都屬於一個統計函數,也能夠使用多個統計函數。
            select * from (select job, deptno, sal, sum(sal) over (partition by deptno) sum_sal, max(sal) over (partition by deptno) max_sal, min(sal) over (partition by deptno) min_sal from emp) pivot (sum(sal) as sum_sal, max(sal) max_sal for job in('PRESIDENT' as PRESIDENT_job, 'MANAGER' as MANAGER_job, 'ANALYST' as ANALYST_job, 'CLERK' as CLERK_job, 'SALESMAN' as SALESMAN_job)) order by deptno;
        此時發現數據被拆分了。
        如今只是針對job字段實現的分組,這樣並非很好,但願有多個字段進行分組,爲了知足要求,能夠這麼寫,增長一個sex列,同時更新81年僱傭的僱員性別都爲女,因而有了以下幾個語句:
            alter table emp add(sex varchar2(10) default '男');
            update emp set sex = '女' where to_char(hiredate,'yyyy') = 1981; commit;
        這個時候就增長了一個性別的列。

    8.4 行列轉換——範例
        設置多個統計列。
            select * from (select deptno, job, sex, sal from emp) pivot (sum(sal) as sum_sal, max(sal) max_sal for (job,sex) in (('MANAGER','男') as MANAGER_male_job, ('MANAGER','女') as MANAGER_female_job, ('CLERK','男') as CLERK_male_job ('CLERK','女') as CLERK_female_job)) order by deptno;

九、設置數據層次
    要求:
        瞭解level...connect by子句的使用。
    設置層次函數:
        層次查詢是一種較爲肯定數據行之間關係結構的一種操做,例如:在現實社會的工做之中必定會存在「管理層」、「職員層」這樣額基本分層關係,幸運的是在Oracle之中用戶也能夠利用其自身所帶的工具實現這樣的層次組織。

    語法:
        level ... connect by [nocycle] prior 鏈接條件 [start with 開始條件]

    語法組成:
        level:能夠根據數據所處的層次結構實現自動的層次編號,例如:一、二、3;
        connect by:指的是數據之間的鏈接,例如:僱員數據依靠mgr肯定其領導,就是一個鏈接條件,其中nocycle須要結合connect_by_iscycle僞劣肯定出父子節點循環關係;
        start with:根節點數據的開始條件。

    9.1 數據層次——範例
        觀察分層的基本關係。
            select empno, ename, mgr, level from emp connect by prior empno = mgr start with mgr is null;網絡

        從如今的結構上以爲返回值沒有層次,能夠改成:
            select empno, lpad('|-', level * 2, ' ') || ename, mgr, level from emp connect by prior empno = mgr start with mgr is null;

        如今的操做是從KING開始的(mgr = null),而後按照領導的結構進行分層。

    9.2 connect_by_isleaf僞列
        在一個樹狀結構之中,節點會分爲兩種:根節點和葉子節點,用戶能夠利用「connect_by_isleaf」僞列判斷某一節點是根節點仍是葉子節點,若是此列返回的是數字0,則表示根節點,若是返回1,則表示葉子節點。
            select empno, lpad('|-', level * 2, ' ') || ename, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '  葉子節點') from emp connect by prior empno = mgr start with mgr is null;

    9.3 connect_by_root列
        connect_by_root的主要做用是取得某一字段在本次分層之中的根節點數據名稱,例如:若是按照領導層次劃分,則全部數據的根節點都應該是KING。
            select empno, lpad('|-', level * 2, ' ') || ename, mgr, level, connect_by_root ename from emp connect by prior empno = mgr start with mgr is null;
        最大的根節點的數據就是KING的信息,如今換一種方式,從一個指定的僱員信息開始。
            select empno, lpad('|-', level * 2, ' ') || ename, mgr, level, connect_by_root ename from emp connect by prior empno = mgr start with empno = 7566;

    9.4 sys_connect_by_path(列, char)函數
        利用「sys_connect_by_path()」函數按照給出的節點關係,自動將當前根節點中的全部相關路徑進行顯示。
        使用sys_connect_by_path()函數取得節點路徑信息。
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點') isleaf from emp connect by prior empno = mgr start with mgr is null;

    9.5 數據層次——範例
        去掉某一節點,將7698節點去掉。
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點') isleaf from emp connect by prior empno = mgr and empno != 7698 start with mgr is null;
        
    9.6 order siblings by 字段
        在使用層次查詢進行數據顯示時,若是用戶直接使用order by子句進行指定字段的排序,有可能會破壞數據的組成結構。
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點') isleaf from emp connect by prior empno = mgr start with mgr is null order by ename;
        那麼如今但願保持結構的排序,能夠使用order siblings by字段:
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點') isleaf from emp connect by prior empno = mgr start with mgr is null order siblings by ename;

    9.7 connect_by_iscycle僞列
        在進行數據層次設計的過程之中,最爲重要的是根據指定的數據列肯定數據間的層次關係,可是有時候也可能出現死循環,例如:KING的領導是BLAKE,而BLAKE的領導是KING就表示一個循環關係,爲了判斷循環關係的出現,在ORacle中也提供了一個connect_by_iscycle僞列,來判斷是否會出現循環,若是出現循環,則顯示1,不然顯示0,同事若是想要判斷是否爲循環節點,則還須要「nocycle」的支持。
        本來KING沒有領導,可是爲了發現問題,給KING添加一個領導,將領導設置爲BLAKE。
            update emp set mgr = 7698 where empno = 7839;
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點') isleaf from emp connect by prior empno = mgr start with empno = 7839 order siblings by ename;
        錯誤提示:ORA-01436: 用戶數據中的 CONNECT BY 循環
        此時因爲存在了循環關係,因此沒法顯示數據,能夠設置nocycle禁止循環:
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點')isleaf from emp connect by nocycle prior empno = mgr start with empno = 7839 order siblings by ename;
        設置了nocycle以後循環就取消了,至少能夠正常顯示了。

        當不肯定是否存在循環時,能夠經過connect_by_iscycle僞列來判斷是否存在循環:
            select empno, lpad('|-', level * 2, ' ') || sys_connect_by_path(ename, '=>') empname, mgr, level, decode(connect_by_isleaf, 0, '根節點', 1, '葉子節點')isleaf, decode(connect_by_iscycle, 0, '【√】沒有循環', 1, '【×】存在循環') from emp connect by nocycle prior empno = mgr start with empno = 7839 order siblings by ename;






9、數據更新
    在SQL語句之中,數據的操做語言(DML)由兩個部分組成:查詢(DQL)、更新操做(增長、修改、刪除)。
    在Oracle數據庫中,一直使用的數據表就是emp表和dept表,那麼這兩張表往後還會有其餘用處,爲了保證表中的數據不被破壞,因此如今將將使用到的表複製一份,主要使用的是emp表。

一、更新前的準備操做
    1.1 複製emp表
        create table emp1 as select * from emp;
        須要注意的是,數據存在,可是卻沒有約束,因此更新的時候能夠自由一些。

二、數據的增長操做
    目標:
        能夠爲數據表添加新的數據。
        將子查詢結果做爲增長數據。

    對於全部的更新操做,與查詢操做最大的不一樣在於其語法幾乎都是固定的。

    語法:
        數據增長操做指的是向數據表中添加一條新的記錄,而對於數據的插入一般有兩種形式:
            形式一:插入一條新的數據
                insert into 表名 [(列1, 列2, ... )] values (值1, 值2, 值3, ... );
            形式二:插入子查詢的返回結果
                insert into 表名 [(列1, 列2, ... )] 子查詢;
        所接觸到的數據主要有三種(varchar二、number、date),因此此時對於這三種數據在增長於法之中的編寫要求以下:
            number類型:直接編寫,如:123;
            varchar2類型:使用「 ' 」聲明,例如:'Oracle'(clob類型也按照一樣的方式進行);
            date類型:能夠按照已有的日期格式編寫字符串,例如:'08-10月 -2016',或者是使用to_date()函數將字符串變爲date類型數據,而若是爲當前日期時間,則直接使用sysdate便可。

    2.1 增長數據——範例
        向emp1數據表之中增長一條新的數據。
        推薦:使用完整語法進行操做,數據增長時須要寫上要增長數據的列的名稱。
            insert into emp1 (empno, job, hiredate, ename, mgr, sal, comm, deptno) values (8888, 'CLERK', sysdate, '王彥超', 7369, 800, 100, 20);
         不推薦:使用簡化語法進行操做,增長數據時須要按照列的順序增長,不然將出現錯誤。

            insert into emp1 values (9999, '王彥超2', 'MANAGER', 7369, to_date('2016-10-08', 'yyyy-mm-dd'), 1000, 100, 20); 

        完整寫法必需要加上要插入的列名稱。
       能夠發現,若是沒有寫上列名稱,實際上處理起來的複雜度是很高的,由於必須考慮順序,因此在開發中,建議使用完整語法,並且在標準項目開發中,必定要使用完整語法。


    2.2 增長數據——範例
        增長一個沒有領導、部門、獎金的新僱員。
            推薦:使用完整語法完成,編寫時只編寫所須要的數據列。
                insert into emp1 (empno, ename, job, hiredate, sal) values (6616, '李楠', 'CLERK', to_date('2016-10-07', 'yyyy-mm-dd'), 600);

            不推薦:使用簡化語法完成,對於須要設置null的數據,要明確寫出null。
                insert into emp1 values (6616, '李楠', 'CLERK', null, to_date('2016-10-07', 'yyyy-mm-dd'), 600, null, null);

        對於沒有增長數據的部分,自動使用null值來表示。

    2.3 增長子查詢結果數據
        子查詢的數據實際上也是一張表的結構,因此能夠直接將這些數據保存到指定表中。
        經過子查詢增長emp1表數據。
            編寫完整格式將全部20部門僱員的信息插入到emp1表之中:
                insert into emp1 (empno, ename, job, mgr, hiredate, sal, comm, deptno) select * from emp where deptno = 20;
            編寫簡寫格式將10部門僱員的信息插入到emp1表之中:
                insert into emp1 select * from emp where deptno = 10;

    2.4 小結
        增長數據時建議使用完整語法,這樣能夠增長代碼的可維護性。

三、數據的更新操做
    目標:
        掌握數據更新操做的語法。

    若是說如今發現表中的數據須要修改,那麼就必須使用update語句進行更新操做。

    數據庫的更新操做主要是指的對數據表中的數據進行修改,與數據的增長同樣,在數據修改的時候有兩種形式:
        形式一:由用戶本身指定要更新數據的內容
            update 表名 set 字段 = 值 [, 字段 = 值] [where 更新條件]
        形式二:基於子查詢的更新
            update 表名 set (column, column, ... ) = (select column, column, ... from table where 查詢條件)

    3.1 數據更新——範例
        將smith(僱員編號爲7369)的工資修改成3000元,而且每月有500元的獎金。
            update emp1 set sal = 3000, comm = 500 where ename = 'SMITH';

    3.2 數據更新——範例
        將工資低於公司平均薪金的僱員的基本工資上漲20%。
            update emp1 set sal = sal * 1.2 where sal < (select avg(sal) from emp);
        公司的平均薪金須要經過avg()函數統計獲得。

        若是此時在更新時,沒有寫出更新條件,那麼表示全部記錄都被更改,若是真的執行了這樣的操做,那麼會出現一個問題:
            假設如今數據表中有500萬條記錄,那麼按照每一條更新的時間爲0.01s,那麼這500萬條記錄的更新時間是50000秒,等於883分鐘,等於13小時,那麼就意味着在這13個小時以內,全部的數據都沒法被其餘用戶修改(數據庫的鎖機制)。因此這種更新所有的操做是不可能出現的,可是若是在現實生活中出現了此類問題並不是沒有辦法。對於軟件問題的解決,實際上就只有兩句話:時間換空間,空間換時間。例如3D的動畫渲染,必定要花費很長的時間,假設一部動畫片須要一個月才能夠所有渲染完成,可是有可能已經成功了29天,可是在第30天的時候發現出錯了,以前所花費的時間就都浪費了。若是此時不但願等待,那麼就增長電腦(空間換時間)。

    3.3 數據更新——範例
        將僱員7369的職位、基本工資、僱用日期更新爲與7839相同的信息。
            update emp1 set (job, sal, hiredate) = (select job, sal, hiredate from emp where empno = 7839) where empno = 7369;

    3.4 小結
        數據更新時能夠直接設置更新數據也能夠經過子查詢取得更新數據。

四、數據的刪除操做
    目標:
        掌握刪除的操做語法。

    當數據表中的某些數據再也不須要時,就能夠經過刪除語句進行刪除,刪除語句的語法以下所示。
        delete from 表名 [where 刪除條件];
        在刪除數據時若是沒有指定刪除條件,那麼就表示刪除所有數據,而對於刪除條件,用戶也能夠編寫子查詢完成。

    4.1 數據刪除——範例
        刪除僱員編號是7566的僱員信息。
            delete from emp1 where empno = 7566;

    4.2 數據刪除——範例
        刪除30部門的全部僱員。
            delete from emp1 where deptno = 30;

    4.3 數據刪除——範例
        刪除僱員編號爲736九、756六、7788的僱員信息。
            delete from emp1 where empno in (7369, 7566, 7788);

    4.4 數據刪除——範例
        刪除公司工資最高的僱員。
            delete from emp1 where sal = (select max(sal) from emp);

        除了這些以外,對於日期類型的數據也能夠在刪除中做爲條件進行判斷。

    4.5 數據刪除——範例
        刪除全部在1987年僱傭的僱員。
            delete from emp1 where extract(year from hiredate) = '1981';

    4.6 小結
        設置的刪除條件能夠指定具體的數值也能夠設置子查詢。

        對於更新的三個操做:增長、修改、刪除,每一次都必定會返回當前操做所影響到的數據行,若是學習過Java,必定要與JDBC操做聯繫在一塊兒,在JDBC操做中更新數據的操做Statement和PrepareStatement兩個接口,調用的方法仍是executeUpdate(),返回的是一個int型數據,就是接收更新的行數。



10、事務

一、事務處理
    目標:
        瞭解事務的特性;
        掌握Oracle中事務處理的相關操做命令。

      圖片

        若是如今斷開已有的鏈接,會出現以上的窗口,實際上這個就表示詢問是否處理事務問題,這一特徵在Oracle11g版本的時候尚未。

    1.1 什麼是事務:
        事務處理在數據庫開發中有着很是重要的做用,所謂事務核心概念就是指一個SESSION所進行的全部更新操做要麼所有成功,要麼所有失敗。事務自己具備:原子性(Atomicity)、一致性(Consistency)、隔離性或獨立性(Isolation)、持久性(Durability),以上的四個特徵,也被稱爲ACID特徵。

        原子性:
            整個事務中的全部操做,要麼所有完成,要麼所有不完成,不可能停滯在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。

        一致性:
            一個事務能夠封裝狀態改變(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,無論在任何給定的時間併發事務有多少。
            也就是說:若是事務是併發多個,系統也必須如同串行事務同樣操做。其主要特徵是保護性和不變性(Preserving an Invariant),以轉帳案例爲例,假設有五個帳戶,每一個帳戶餘額是100元,那麼五個帳戶總額是500元,若是在這個5個帳戶之間同時發生多個轉帳,不管併發多少個,好比在A與B帳戶之間轉帳5元,在C與D帳戶之間轉帳10元,在B與E之間轉帳15元,五個帳戶總額也應該仍是500元,這就是保護性和不變性

        隔離性:
            相似於Java中的同步鎖。
            隔離狀態執行事務,使它們好像是系統在給定時間內執行的惟一操做。若是有兩個事務,運行在相同的時間內,執行相同的功能,事務的隔離性將確保每一事務在系統中認爲只有該事務在使用系統。這種屬性有時稱爲串行化,爲了防止事務操做間的混淆,必須串行化或序列化請求,使得在同一時間僅有一個請求用於同一數據。

        持久性:
            當系統崩潰,事務依然能夠提交,不受系統控制,受磁盤文件的控制。在事務完成之後,該事務對數據庫所做的更改便持久的保存在數據庫之中,並不會被回滾。

    Oracle中事務操做命令:
        set autocommit = off
            取消自動提交處理,開啓事務處理。
        set autocommit = on
            打開自動提交處理,關閉事務處理。
        commit
            提交事務。
        rollback to [回滾點]
            回滾操做。
        savepoint 事務保存點名稱
            設置事務保存點。
            
    1.2 關於SESSION:
        在Oracle數據庫之中,每個鏈接到此數據庫的用戶都是一個「SESSION」,每個SESSION都擁有獨立的事務,均可以使用事務操做命令,不一樣的SESSION事務是徹底隔離的。

         圖片

        爲了更好的觀察事務的特色,因此使用多個SQLPLUS窗口進行功能的展現:

圖片

        此時emp1一共存在了14行記錄,第一個SESSION將執行如下的刪除操做:
            刪除僱傭年數超過32年的員工。
                delete from emp1 where months_between(sysdate,hiredate)/12 > 32;
            能夠發現第一個SESSION刪除了12行數據,同時查看一下第一個SESSION中emp1表所剩數據。

圖片

        第二個SESSION打開窗口,執行查詢操做。
            select * from emp1;

圖片

        能夠發現數據應該已經被刪除了,可是如今依然存在,這就是事務的一個特徵:更新緩衝。

    1.3 更新緩衝
        對於每個SESSION而言,每個數據庫的更新操做在事務沒有被提交以前都只是暫時保存在了一段緩衝區之中,並不會真正的向數據庫發出命令,若是如今用戶發現操做有問題了,則能夠進行事務的回滾。

         圖片

        在第一個SESSION之中所執行的更新操做並無真正發出,由於若是出現錯誤,要留有一個挽回的餘地,因此在其餘SESSION使用的時候,數據都是原始的數據。

        若是以爲刪除操做有誤,那麼能夠在第一個SESSION中使用rollback回滾操做。
        可是若是在刪除操做的同時使用commit命令提交事務,那麼就表示真正的向數據庫發出了更新命令,此時第二個SESSION再查詢emp1表的所有數據,會發現數據也被更改了,因此說,只有在commit以後,更新操做纔會真正的執行。
        在沒有更新前,執行了rollback操做,那麼會回滾到原點上,爲了方便操做,Oracle數據庫提供了保存點(SAVEPOINT)。

    1.4 回滾存儲點
        在默認狀況下,執行rollback命令意味着所有的操做都要回滾,若是如今但願能夠回滾到指定操做的話,能夠採用SAVEPOINT設置保存點,這樣在回滾的時候,就能夠經過rollback回滾到指定的保存點上。

         圖片

        insert into emp1 (empno, ename, hiredate, job, sal) values (1234, '王彥超', sysdate, 'CLERK', 800);
        update emp1 set sal = 5000 where empno = 1234;
        savepoint sp_a;
        insert into emp1 (empno, ename, hiredate, job, sal) values (5678, '奧巴馬', sysdate, 'MANAGER', 2000);
        update emp1 set job = '總監' where empno = 5678;
        savepoint sp_b;
        delete from emp1;
        rollback 保存點;

        能夠發現,只要有保存點,就能夠準確的回到保存點所保存的操做中。

    1.5 事務自動提交
        設置事務是否自動提交:
            set autocommit [on | off];

        默認狀況下,全部的事務都不屬於自動提交,必須由用戶手動提交,若是但願自動提交,也就是說每執行一個原子性操做,就自動提交一次事務,就必須將事務設爲自動提交。

    1.6 小結
        掌握事務的處理命令:commit、rollback。

二、鎖
    目標:
        掌握所的基本概念。
        理解行級鎖定與表級鎖定。

    實際上所謂的鎖指的就是不一樣的SESSION同時操做了同一資源所引起的問題。

    2.1 範例
        第一個SESSION執行了如下語句:
            select * from emp1 where deptno = 10 for update;
        第二個SESSION執行一樣的語句:
            能夠看見程序進入阻塞狀態。
        由於數據只可以被一個SESSION操做,當鎖中SESSION執行ROLLBACK以後,第二個SESSION就能夠訪問了。
         圖片

    2.2 鎖的分類
        在Oracle中的鎖有兩種基本類型。
            行級鎖定:
                又稱爲記錄鎖定,對當前事務中的一行數據以獨佔的方式進行鎖定,在此事務結束以前,其餘事務要一直等待該事務完結,例如2.1所演示的就是行級鎖定。
                當記錄被鎖定以後,依然能夠進行查詢操做,可是不能被另外一個SESSION鎖定。
            表級鎖定:
                對整張數據表進行數據鎖定,只容許當前事務訪問數據表,其餘事務沒法訪問。

    2.3 行級鎖定
        當用戶執行了insert、update、delete以及select for update語句時,Oracle將隱式的實現記錄的鎖定,這種鎖定被稱爲排它鎖。
        這種鎖的主要特色是:
            當一個事務執行了相應的數據操做以後,若是此時事務沒有提交,那麼會一直以獨佔的方式鎖定這些操做的數據,其餘事務一直到此事務施放鎖後才能夠進行操做。

        第一個SESSION更新7788的僱員信息。
            update emp1 set sal = 6000 where empno = 7788;
        第二個SESSION也更新7788的僱員信息。
            update emp1 set job = 'MANAGER' where empno = 7788;
        此時更新的記錄和第一個SESSION的操做是相同的,並且第一個SESSION沒有提交事務。
        若是第二個SESSION更新了同一行記錄,也會遇到鎖,進入阻塞狀態。

    2.4 表級鎖定
        表級鎖定須要用戶明確的使用"LOCK TABLE"語句進行手動鎖定。
        語法:
            LOCK TABLE 表名 | 視圖名, 表名 | 視圖名, ... in 鎖定模式 mode [nowait];
        nowait:
            這是一個可選項,當試圖鎖定一張數據表時,若是發現已經被其它事務鎖定,不會等待。
        鎖定模式有以下幾種常見模式:
            row share:
                行共享鎖,在鎖按期間容許其餘事務併發對錶進行各類操做,但不容許任何事務對同一張表進行獨佔(禁止排它鎖)。
            row exclusive:
                行排它鎖,容許用戶進行任何操做,與行共享鎖不一樣的是它不能防止別的事務對同一張表進行手動鎖定或獨佔操做。
            share:
                共享鎖,其餘事務只容許執行查詢操做,不容許執行更新操做。
            share row exclusive:
                共享排它鎖,容許任何用戶進行查詢操做,但不容許其餘用戶使用共享鎖,以前所使用的"select from update"就是共享排它鎖的常見應用。
            exclusive:排它鎖,事務將以獨佔方式鎖定表,其餘用戶容許查詢,可是不能修改,也不能設置任何的鎖。

    2.5 表級鎖定——範例
        在第一個SESSION對emp1表使用共享鎖。
            lock table emp1 in share mode nowait;
            此時,鎖定完成後,第二個SESSION若是想要執行查詢操做是沒有任何問題的。
        第二個SESSION刪除emp1表所有數據。
            delete from emp1;
        第二個SESSION進入阻塞狀態。

    2.6 解除鎖定
        儘管用戶清楚了鎖產生的緣由,可是在不少時候因爲業務量的增長,可能並不會爲用戶清楚的羅列出現鎖的種種可能,因此此時就必須經過其餘方式查看是否出現了鎖定以及經過命令手動的解除鎖定。
        解除鎖定語法:
            alter system kill session 'sid, serial#'
        在此格式之中發現若是想要結束一個SESSION(結束一個SESSION就表示解鎖),則須要兩個標記:SESSION ID(SID),另一個就是序列號(SERIAL#),而這兩個內容能夠利用"v$locked_object"和"v$session"兩個數據字典查詢獲得。

    2.7 使用管理員查看鎖的狀況
        第一個SESSION使用for update鎖定數據:
            select * from emp1 where deptno = 10 for update;
        第二個SESSION執行相同操做:
            select * from emp1 where deptno = 10 for update;
        因而如今就出現了鎖的狀況,下面就查看鎖的問題,可是普通用戶沒法查看,必須是超級管理員才能查看(SYS)。
        查看數據字典:
            select * from v$locked_object;

             圖片

            select session_id, oracle_username, process from v$locked_object;

             圖片

        此處會查詢到一個SESSION_ID(每個用戶的SESSION由管理員分配)。可是隻知道SESSION_ID還沒法解除鎖定,因此還必須查看v$session的數據字典。
            select * from v$session where sid in (67, 194);
        可是由於數據太多,因此指定字段進行查詢:
            select sid, serial#, lockwait, status from v$session where sid in (67, 194);

             圖片

        active:
            處於此狀態的會話,表示正在執行,處於活躍狀態。
        inactive:
            處於此狀態的會話表示不是正在執行的。
        killed:
            處於此狀態的會話,被標註爲刪除,表示出現了錯誤,正在回滾。

    2.8 KILL一個線程(SESSION)(解除死鎖)
        alter system kill session '194, 185';
        此時,鎖定的一方就會出現被KILL SESSION的提示:

             圖片

        當管理員執行完該命令以後,若是該SESSION再執行其餘命令,會顯示:
            SQL> select * from emp1 where deptno = 10 for update;
            ERROR:
            ORA-03114: 未鏈接到 ORACLE

    2.9 小結
        鎖是在多個SESSION訪問同一資源時出現的狀態。
        鎖分爲兩類:行級鎖定和表級鎖定。





11、變量

一、替代變量
    替代變量只是Oracle中的一種靈活概念,與實際的開發關係並不大。
    目標:
        理解替代變量的基本概念。
        理解替代變量定義。
        瞭解ACCEPT指令。

    替代變量的操做就相似於鍵盤輸入數據的操做。

    1.1 替代變量——範例
        驗證替代變量的使用,查詢工資大於2000的員工信息。
            select * from emp where sal > 2000;
        發現此時要進行操做的數據是固定的,能不能有一種方式,由用戶動態的來決定數值呢?因此這就屬於替代變量的應用範疇。
             select * from emp where sal > &inputsal;
        此時使用了一個替代變量,因而在SQLDeveloper中運行效果以下:

             圖片

        若是此處輸入的值爲2000,那麼在替代變量的地方的數值就變爲了2000。
        可是發現利用此工具表現的不是很明顯,因此仍是使用sqlplus完成。

圖片

        以上輸入的是一個數字,還能夠輸入字符串。

    1.2 替代變量——範例
        查詢一個僱員的僱員編號、姓名、職位、僱用日期、基本工資,查詢的僱員姓名由用戶輸入:
            select empno, ename, job, hiredate, sal from emp where ename = &inputename;
        字符串還須要使用單引號「 ' ’」,因此此時還須要輸入單引號,過於麻煩,那麼就在定義替代變量的時候準備好單引號:
            select empno, ename, job, hiredate, sal from emp where ename = '&inputename';
        可是這個時候還存在一個問題,輸入數據的時候確定不會考慮大小寫問題,既然數據表中的數據都使用了大寫字母表示,那麼就直接將用戶輸入的所有內容經過upper()函數轉換爲大寫便可:
            select empno, ename, job, hiredate, sal from emp where ename = upper('&inputename');

    1.3 替代變量——範例
        根據僱員姓名額關鍵字(由用戶輸入)查詢僱員編號、姓名、職位、僱用日期、基本工資。
            select empno, ename, job, hiredate, sal from emp where ename like '%&inputkeyword%';
 
    1.4 替代變量——範例
        由用戶輸入僱用日期,要求查詢出全部早於此僱用日期的僱員編號、姓名、職位、僱傭日期、基本工資。
            select empno, ename, job, hiredate, sal from emp where hiredate < to_date('&inputhiredate', 'yyyy-mm-dd');
        若是輸入的是日期,用戶只可以輸入字符串,並且中國的習慣是以「年 - 月 - 日」的方式輸入,因此必須使用to_date()函數進行轉換。

    1.5 替代變量——範例
        輸入查詢僱員的職位及工資(高於輸入工資)信息,而後顯示僱員編號、姓名、職位、僱用日期、基本工資。
            select empno, ename, job, hiredate, sal from emp where job = '&inputjob' and sal > &inputsal;

    1.6 替代變量的詳細說明
        在以前只是在where子句中使用替代變量,可是在SQL的任意子句中均可以使用替代變量,因此下面經過幾個程序來演示一下,不一樣子句中使用替代變量的操做。

        在SELECT子句中使用替代變量:
            select &inputColumnName from emp where deptno = &inputDeptno;

        在FROM子句中使用替代變量:
            select * from &inputTableName;

        在order by子句中使用替代變量:
            select * from emp order by &inputOrderByColumn desc;

        在group by子句中使用替代變量:
            select &inputGroupByColumn, sum(sal) from emp group by &inputGroupByColumn;
                在分組查詢之中,select子句裏面能夠出現的字段必定是group by子句中規定的分組字段。
            分別輸入:job,deptno
                此時出現了錯誤,由於沒有任何一個限定操做可讓兩個輸入保持一致,實際上也沒法限定,可是若是如今只輸入一次,那麼這個問題就應該好解決了,能夠使用「&&」替代變量。
            select &&inputGroupByColumn, sum(sal) from emp group by &inputGroupByColumn;
            輸入:job,job
            使用「&&」的替代變量,只要求在第一次使用它的時候進行輸入,若是再次使用,那麼就不須要重複輸入。
            可是如今也會出現一個問題,若是如今輸入的再也不是job,而是deptno,那麼就沒法輸入了,由於&&inputGroupByColumn在一個SESSION內只有一個值。因此有兩種操做來解決此問題:第一種是關閉窗口從新打開,第二種是執行UNDEFINE命令:
                UNDEFINE inputGroupByColumn;
            而後就能夠正常操做了。

        若是不須要任何的替代變量的定義,那麼能夠輸入set define off命令:
            set define off;

        在SQL*Plus中默認的"&"表示替代變量,也就是說,只要在命令中出現該符號,SQL*Plus就會要你輸入替代值。這就意味着你沒法將一個含有該符號的字符串輸入數據庫或賦給變量,如字符串「SQL&Plus」系統會理解爲以「SQL」打頭的字符串,它會提示你輸入替代變量 Plus的值,若是你輸入ABC,則最終字符串轉化爲「SQLABC」。 
        set define off    則關閉該功能,「&」將做爲普通字符,如上例,最終字符就爲「SQL&Plus」 
        set define off    關閉替代變量功能 
        set define on    開啓替代變量功能 
        set define *       將默認替代變量標誌符該爲「*」(也能夠設爲其它字符)
    1.7 定義替代變量         在Oracle中除了能夠使用「&&」定義替代變量以外,還能夠使用DEFINE命令來定義替代變量,用戶能夠利用DEFINE建立一個字符型的替代變量,並且這種方式定義的替代變量會一直保存到一個SESSION的操做結束或者使用UNDEFINE清除變量。         DEFINE命令格式:             DEFINE 替代變量名稱 = 值;             範例:DEFINE inputdname = 'ACCOUNTING';             查詢替代變量的內容:DEFINE inputdname;             使用DEFINE定義的替代變量:select * from dept where dname = '&inputdname';         UNDEFINE命令格式:             清除inputdname替代變量內容:UNDEFINE inputdname;     1.8 替代變量——範例         定義一個替代變量:             DEFINE inputdname = 'ACCOUNTING';         查詢此替代變量:             define INPUTDNAME;             輸出:DEFINE INPUTDNAME =  "ACCOUNTING" (CHAR)         使用此定義的替代變量:             select * from dept where dname = '&inputdname';         清除替代變量:             若是如今再也不須要指定的替代變量,那麼就使用undefine命令完成。             undefine inputdname;     1.9 ACCEPT命令         使用ACCEPT命令能夠指定替代變量的提示信息。         ACCEPT語法格式:             ACCEPT 替代變量名稱 [數據類型] [FORMAT 格式] [PROMPT '提示信息'] [HIDE]         ACCEPT語法中各個參數的做用以下所示:             替代變量名稱:                 存儲值的變量名稱,若是該變量不存在,則由SQL*Plus建立該變量,可是在定義此替代變量名稱前不能加「&」。             數據類型:                 能夠是NUMBER、VARCHAR或者是DATE類型數據。             FORMAT格式:                 指定格式化模型,例如A10或9.99。             PROMPT提示信息:                 用戶輸入替代變量時的提示信息。             HIDE隱藏輸入內容:                 例如在輸入密碼時隱藏輸入內容。         若是想要使用ACCEPT命令,那麼必須結合腳本文件完成。     1.10 觀察ACCEPT命令的操做         將如下命令保存爲sql腳本,放到D盤根目錄下,起名爲wyc.sql:             accept inputEname PROMPT '請輸入要查詢信息的僱員姓名:'select empno, ename, job, hiredate, sal from emp where ename  = upper('&inputEname');         使用:@d:\wyc.sql命令執行sql腳本。     1.11 使用Accept定義替代變量         accept inputGroupByColumn PROMPT '請輸入要分組的字段:' select &&inputGroupByColumn, sum(sal) from emp group by &inputGroupByColumn;     1.12 使用HIDE隱藏輸入         如今發現全部輸入的數據都被明文顯示了,有時候不但願其餘用戶知道輸入內容,能夠使用HIDE進行隱藏:             accept inputGroupByColumn PROMPT '請輸入要分組的字段:' HIDE select &&inputGroupByColumn, sum(sal) from emp group by &inputGroupByColumn;     1.13 使用FORMAT格式化輸入         使用FORMAT限定輸入的數據長度:             accept inputGroupByColumn PROMPT '請輸入要分組的字段:' FORMAT A10 select &&inputGroupByColumn, sum(sal) from  emp group by &inputGroupByColumn;         A10表示輸入數據長度不能超過10個字符。         使用FORMAT格式化輸入格式:             accept inputDate DATE FORMAT 'yyyy-mm-dd' PROMPT '請輸入要查詢的日期:' select empno, ename, job, hiredate from emp where hiredate = to_date('&inputDate', 'yyyy-mm-dd');             須要注意的是ACCEPT命令不能換行,不然出錯。     1.14 小結         使用替代變量能夠提升數據操做的交互性。 
相關文章
相關標籤/搜索