sql 幾點記錄

 

1       With子句

1.1     學習目標

掌握with子句用法,而且瞭解with子句可以提升查詢效率的緣由。sql

1.2     With子句要點

  1. with子句的返回結果存到用戶的臨時表空間中,只作一次查詢,反覆使用,提升效率。
  2. 若是定義了with子句,而在查詢中不使用,那麼會報ora-32035 錯誤:未引用在with子句中定義的查詢名。
  3. 前面的with子句定義的查詢在後面的with子句中可使用。可是一個with子句內部不能

嵌套with子句。oracle

  1. 當一個查詢塊名字和一個表名或其餘的對象相同時,解析器從內向外搜索,優先使用子查詢塊名字。
  2. with查詢的結果列有別名,引用的時候必須使用別名或*。
  3. with有可能影響執行計劃。

1.3     with子句語法

 

With alias_name as (select1), --as和select中的括號都不能省略函數

alias_name2 as (select2),--後面的沒有with,逗號分割,同一個主查詢同級別地方,with子學習

查詢只能定義一次測試

優化

alias_namen as (select n) –與下面的實際查詢之間沒有逗號指針

Select ….code

1.4 with使用例子:

  1. 最簡單的使用方法:

如查詢部門名稱包含「A」的全部員工信息對象

--with clause排序

with a as

(select deptno from dept where dname like '%A%')

select * from emp where deptno in (select * from a);

with a as

(select deptno from dept where dname like '%A%'),--a結果集

a2 as(select * from a where deptno>20)--a1結果集直接從a中篩選

select * from emp where deptno in (select * from a2);

 

  1. 多層同級只能用一個with,而且後面的結果集可使用前面的結果集:

查詢部門名稱包含「A」而且部門編號大於20的全部員工信息

with a as

(select deptno from dept where dname like '%A%'),--a結果集

a2 as(select * from a where deptno>20)--a1結果集直接從a中篩選

select * from emp where deptno in (select * from a2);

 

  1. 不一樣級查詢可使用多個with:

查詢部門名稱包含「A」而且部門編號大於20的全部員工信息的另一種實現方式以下

with a as

(select deptno from dept where dname like '%A%')--a結果集

select * from emp where deptno in (--括號內層做爲子查詢,爲第二級

with a2 as(select * from a where deptno>20)--a1結果集直接從a中篩選

select * from a2

);

1.5     使用場景

那什麼狀況下能使用到with子句呢?如下我就舉幾個簡單的例子,簡單的說明如下:

  1. 我想測試一句sql,而我不想專門創建一個測試表:

我想測試成績大於90的學生,我不想創建學生表,能夠用到with子句

with stu as(

select '張娜' sname,99 score from dual union

select '王傑' ,35  from dual union

select '宋麗' ,85  from dual union

select '陳曉' ,73  from dual union

select '李元' ,100  from dual

)--with 組成一個臨時的結果集,存放在用戶的臨時表空間

select  * from stu where score>90

  1. 當一個sql重複用到某個相同的結果集做爲子查詢:

--查詢銷售部工資>1500或者銷售部工資小於1000的員工

select * from emp where deptno=(select deptno from dept where dname ='SALES') and sal >1500

union all

select * from emp where deptno=(select deptno from dept where dname ='SALES') and sal <1000

--以上sql select deptno from dept where dname ='SALES'須要執行兩次,影響效率

--可使用with優化一下

with salno as(select deptno from dept where dname ='SALES')

select * from emp where deptno=(select * from salno) and sal >1500

union all

select * from emp where deptno=(select * from salno) and sal <1000

2       集合操做

2.1     學習目標

掌握union,union all,minus,intersect的使用,可以描述集合運算,瞭解內部運行原理。

2.2     要點

 

Union all 效率通常比union高。Union all內部不作排序工做,也不作剔除

重複行工做,而union則作這個工做。因此當數據量比較大的時候,能用union all的時候儘可能用union all。除了union all 默認不作排序和剔除重複行的操做外,

union,minus,intersect都默認按第1個查詢結果的第1列進行升序排列,而且

不包含重複行。

2.3     語法

(select  resource 1)

Union/union all/minus/intersect

(select  resource 2)

Union/union all/minus/intersect

(select  resource 3)

……….

其中查詢結果集的各個字段的類型可以互相兼容,而且總的結果集字段名與第一個結果集相同。

2.4     使用案例

數據準備:

create table t1 as select rownum rn from dual connect by rownum<7;

create table t2 as select rownum+3 rn from dual connect by rownum<7;

                 

  1. 查詢t1和t2表的全部記錄,不去除重複。

 

  1. 查詢t1和t2表的全部記錄,去除重複。

 

 

  1. 查詢t1和t2表都存在的記錄

 

  1. 查詢t1表存在,t2表不存在的記錄

 

  1. 排序操做:

 

  1. 除了union all其餘的所有會在總的結果集中剔除重複,例如:

insert into t1 values(1);

commit;

如今t1表中有兩條相同的記錄,其rn的值爲1。

在進行集合運算時重複的記錄被剔除:

 

2.5     使用場景

當要對多個結果集進行集合操做時,但是使用集合操做。

3       case與decode

3.1     學習目標

會使用case表達式和decode函數,理解各個參數和返回值的含義。

3.2     要點

Case表達式:

  1. When後面的表達式類型應該所有保持一致,返回值類型也必須保持一致,或者可以進行隱式轉換。
  2. case 表達式 when 值,若是值是null,就算表達式也是null,結果也是返回false。也就是case 後面的表達式若是值爲null,不會與when null 匹配,只會與else 匹配。

Decode函數的使用方法與case when類似,可是decode只能用等號匹配。

3.3     語法

Case表達式第一種:

case exp when comexp then returnvalue

..when comexp then returnvalue

Else

Returnvalue

End

Case表達式第二種:

case when Boolean then returnvalue

..when Boolean then return value

Else

Returnvalue

End

Decode函數:

decode(exp,

value1,res1,

value2,res2,….,

valuen resn,

elsevalue)。

3.4     使用案例

Case 第一種用法:

 

Case 第二種用法:

 

Decode用法:

 

上文提到過null,碰到null的時候要注意,好比:

 

這種狀況能夠這樣處理:

 

若是用decode函數:

 

3.5     使用場景

當咱們的sql要求根據不一樣的條件返回不一樣的值時,可使用。

4       exists與in、not exists與not in

4.1     學習目標

掌握exists與in的、not exists與not in的用法,瞭解其內部的執行順序 與執行原理,知道什麼狀況下用exists,什麼狀況下用in。

4.2     要點

  1. Exists 用於只能用於子查詢,能夠替代in,若匹配到結果,則退出內部

查詢,並將條件標誌爲true,傳回所有結果資料。

  1. 若子查詢結果集比較小,優先使用in,若外層查詢比子查詢小,優先使用exists。由於若用in,則oracle 會優先查詢子查詢,而後匹配外層查詢,若使用exists,則oracle 會優先查詢外層表,而後再與內層表匹配。最優化匹配原則,拿最小記錄匹配大記錄。

4.3     語法

In:Select select_fields from table_name where field_name in(select clause);

Exists:Select select_fields from table_name exists (select clause)

4.4     使用案例

查詢員工部門編號在部門表中存在的員工記錄:

 

以上語句能夠用Exist替換:

 

另外not in和not exists在某些狀況下也能夠相互轉換,可是要注意一點,not in中的子查詢返回的結果集包含null值的時候,查詢會失效。例如我想查詢對應員工記錄數爲0的部門。以下:

 

用not exists:

 

以上語句不能用not in替換:

 

查詢失效無記錄返回。注意這並非oracle的bug,由於在oracle中null不表示空,而是表示未知,當使用not in的時候,若是子查詢返回的結果集中包含null值,咱們並不知道外層查詢的記錄在不在子查詢返回的結果集以內,因此無記錄返回。雖然這樣,可是並不表示not in和not exists是徹底不能夠轉換的,好比子查詢所選的字段在對應的表中沒有null值,這時not in和not exists是能夠相互轉換的。或者在某些狀況下內層子查詢加上field_name is not null限制條件也是能夠的。

4.5     使用場景

當內層查詢返回的結果集較小時,用in 或者not in效率較高。當內層子查詢返回的結果集比較大時,用exists或者not exists執行的效率較高。

5       行列互換

5.1     學習目標

掌握列轉行技術和經常使用的行專列技術。

5.2     要點

行專列的狀況有多種,不一樣的狀況側重點也不同。

5.3     語法

5.4     使用案例

  1. 列轉行

第一種方法:須要用到union或者union all:

 

第二種方法:用到model

 

  1. 行專列,如我有escore表用來記錄每一個學生每一個科目的成績,以下:

 

若是我想將每一個學生的成績統計在一行上,如:

3 語文 11 數學 55 英語 66

則我可使用以下sql:

 

這個sql表面上看沒什麼問題,可是仔細看一下三個結果集es、ys和ss,他們來源於同一個表,並且查詢方法也相似,都是根據type的值去篩選的,這樣就會對escore表查詢三遍,嚴重影響查詢速率,那這個sql咱們如何去優化呢!

首先在你的腦海裏面要有一種思路,根據需求,原先每一個學生成績有多行記錄,如今要顯示到一行上,那通常狀況下咱們是須要根據學生分組的。因此group by sid 這個是必定要有的,既然分組那咱們但是使用oracle的聚合函數去求其餘行的數據。至於科目字段目前都是已知的,也就是第2,4,6列顯示的分別是英語、語文、數學這幾個字,是常量,咱們不用去考慮,那剩下的也就是最關鍵的,咱們去求三科的成績就能夠了。
讓咱們再看一下escore表,當指針移到某一行數據時,當type=e時,咱們就取到score,加到第三列上,那第五列和第七列就加0,也就是sum(decode(type,’e’,score,0)),其餘列相似,這樣group by時用到的聚合函數還有decode結合在一塊兒使用,就能夠完成咱們的要求了,sql寫出來時這樣的:

 

  1. 字符串組合的多行轉一列,例如我有一張測試表以下:

 

我想根據id分組,將每一行的name鏈接起來,以下圖是我想要的結果:

 

這種行轉列不是真正意義的行轉列,是多行數據的值拼接後顯示到一列上,那這種狀況怎麼處理呢,首先分析一下:多行id相同的值轉換成一行,通常狀況下須要用到group by,可是對於字符串,oracle中沒有一個聚合函數適合用到此處的字符串鏈接,那該怎麼辦呢?

在oracle中,有sys_connect_by_path(field_name,concat_value)函數,能夠經過connect by來依次鏈接每一行的數據,connect by 的語法是這樣的:

start with field1=1--以當前表達式返回true的行開始

connect by prior field2=field3--經過當前行查找下一行,也就是說某一行數據的field3字段等於當前行的field2,那就把這行數據做爲下一行

    有了這個思路,咱們就能夠用connect by 經過使用sys_connect_by_path(field_name,concat_value)這個函數,而且根據id分組,將字符串鏈接在一塊兒,而後經過max聚合函數,選出每組最長的字符轉就能夠了,那剩下的也就是最關鍵的問題就是我怎樣去使用connect by,經過當前行找到下一行呢?充分發散一下你的思惟,看一下以下結果集:

 

那我下一步用以下思路使用connect by將所要的結果查詢上來:

start with  lg is null--lgnull的行做爲起始行

connect by prior rn=lg and prior id=id --當前行與其餘行比較,知足這個條件的就做爲下一行數據     

總的查詢結果以下:

 

其實怎麼使用connect by 方法不少,例如以下sql也能完成:

相關文章
相關標籤/搜索