走向面試之數據庫基礎:二、SQL進階之case、子查詢、分頁、join與視圖


一、CASE的兩種用法

1.1 等值判斷->相當於switch case

  (1)具體用法模板:

    CASE expression

       WHEN value1 THEN returnvalue1

       WHEN value2 THEN returnvalue2

       WHEN value3 THEN returnvalue3

       ELSE defaultreturnvalue

    END 

  (2)具體使用示例:

  假設我們有一個論壇網站,其中有一張User表{ UId,Name,Level },Level是一個int類型,代表了用戶等級類型,例如:1代表骨灰,2代表大蝦等;我們就可以用CASE來對其進行等值判斷了:

select Name,Rank=(
    case Level
        when 1 then '骨灰'
        when 2 then '大蝦'
        when 3 then '菜鳥'
    end
)    
from User

1.2 條件判斷->相當於if else if else

  (1)具體用法模板:

    CASE

      WHEN condition1 THEN returnvalue1

      WHEN condition2 THEN returnvalue2

      WHEN condition3 THEN returnvalue3

      ELSE defaultreturnvalue

    END

注意:then后面返回的數據類型要一致, returnvalue1、 returnvalue2、 returnvalue3的數據類型必須一致。

  (2)具體使用示例:

  假設我們有一張Score成績表,里面記錄有所有同學的成績,此時我們想要對所有成績進行一個評級,比如成績如果>=90那么則評為A級,>=80且<90則評為B級,這里我們怎來寫呢?

select studentId,rank=(    
    case
        when english between 90 and 100 then 'A'
        when english between 80 and 89 then 'B'
        when english between 70 and 79 then 'C'
        when english between 60 and 69 then 'D'
        when english < 60 then 'E'
        else '缺考'
    end
)
from Score

二、子查詢的用法

2.1 子查詢初步

  就像使用普通的表一樣,被當作結果集的查詢語句被稱為子查詢。所有可以使用表的地方幾乎都可以使用子查詢來代替。例如:我們如果要找到所有計科一班的同學信息,可以首先通過T_Class表找到計科一班的Id,然后再在T_Student表中找到所有ClassId為計科一班Id的行即可。

select * from T_Student where ClassId=
(
    select Id from T_Class where Name='計科一班'
)

2.2 單值子查詢

  只有返回且僅返回一行、一列數據的子查詢才能當成單值子查詢。例如我們上面提到的例子,子查詢中只返回了一個ClassId,這就是單值子查詢。當子查詢跟隨在=、!=、<、<=、>、>=,<> 之后,或子查詢用作表達式,只能使用單值子查詢。

2.3 多值子查詢

  如果子查詢是多行單列的子查詢,這樣的子查詢的結果集其實是一個集合,那么可以使用in關鍵字代替=號。例如:我們如果想快速地在T_Student表中刪除計科一班和計科二班的所有學生記錄,我們可以使用in關鍵字:

delete from T_Student where ClassId in
(
    select Id from T_Class where Name='計科一班' or Name='計科二班'
)

2.4 Exists—你存在我深深的腦海里

  exists是用來判斷是否存在的,當exists查詢中的查詢存在結果時則返回真,否則返回假。not exists則相反。

  exists做為where 條件時,是先對where 前的主查詢詢進行查詢,然后用主查詢的結果一個一個的代入exists的查詢進行判斷,如果為真則輸出當前這一條主查詢的結果,否則不輸出。

  exists后面的查詢稱為相關子查詢,即子查詢的查詢條件依賴於外層父查詢中的某個屬性值,其處理過程一般為:先取外層查詢中的第一個元組,根據它與內層查詢中的相關屬性值處理內層查詢,若where子句返回true,則將此元組放入結果表中,然后取外層查詢中的下一個元組,重復這個過程直到全部檢查完畢為止。

  例如:我們有一張人員信息表,里邊有一個人員類型Id字段(pTypeId),它是一個外鍵,對應着人員類型表的主鍵ptId。如果我們有以下的SQL語句,使用Exists關鍵字則可以有如下的理解:

select * from Employee e where exists 
(select * from EmployeeType et where e.pTypeId=et.ptId)

  那么,在這句SQL的執行過程中,我們可以將其理解為一個雙重的for循環,外邊是主表的循環遍歷,然后將其放到一個temp變量中,再進入從表的for循環,並與從表的項進行一個一個的按照匹配規則(這里是e.pTypeId=et.ptId)進行匹配,如果有匹配成功則返回true,並且將這一行記錄放到要返回的結果集中,否則返回false。

三、手寫分頁SQL代碼

  這里假設每頁的頁大小為10條記錄

3.1 利用Top N進行簡單分頁

  (1)如果我們要獲取第一頁的數據,也就是前10個:

select top 10 * from Account

  (2)現在我們要獲取第一頁之后的數據,也就是第20個~最后一個:

select * from Account where Id not in (select top 10 Id from Account) 

  (3)現在我們對第20個~最后一個的數據集中取前10個也就成為第二頁的數據了:

select top 10 * from Account where Id not in (select top 10 Id from Account) 

  (4)將上述代碼總結為分頁代碼,設頁大小為pageSize,請求頁號為pageIndex:

select top @pageSize * from Account where Id not in (select top ((@pageIndex-1)*@pageSize) Id from Account) 

PS:這種分頁方式的缺點是如果要取很多頁之后的數據,那么就要取出前面很多頁的ID,查詢開銷較大,執行效率也就低下。

  從圖中可以看出,在小數據量的對比下,Top N的查詢開銷較小。但是在大數據量的對比下,Row_Number的方式會取得更高的查詢效率以及較小的開銷。

3.2 利用Row_Number()進行高效分頁

  (1)SQL Server 2005后增加了Row_Number函數,可以簡化分頁代碼的實現。首先,Row_Number()是一個排序函數,它可以生成一個有序的行號(如果單靠ID來排序,中間存在斷層,例如某一個ID行已經被刪除了)。根據MSDN的定義:返回結果集分區內行的序列號,每個分區的第一行從 1 開始。而排序的標准是什么呢?這個就要靠緊跟其后的OVER()語句來定義了。這里我們可以通過一個示例來看看,其生成的行號如何。

select *,ROW_NUMBER() OVER(order by Id) as RowNum from Account

  (2)根據ROW_NUMBER()的使用,我們可以將其應用到分頁上,於是我們可以寫出以下的代碼來實現獲取第二頁的數據集:

select * from (
    select *,ROW_NUMBER() OVER(Order by Id) as rownum from Account
) as t
where t.rownum between 11 and 20
order by t.Id asc

  (3)將上述代碼總結為分頁代碼,設頁大小為pageSize,請求頁號為pageIndex:

select * from (
    select *,ROW_NUMBER() OVER(Order by Id) as rownum from Account
) as t
where t.rownum between (@pageIndex-1)*pageSize+1 and @pageSize*pageIndex
order by t.Id asc

四、各種連接—JOIN

4.1 Join==Inner Join

  默認情況下,使用Join則代表Inner Join內連接,表示兩個表根據某種等值規則進行連接。例如下面示例:查詢所有學生的學號、姓名及所在班級

select p.Id,p.Name,c.Name from T_Person p join T_Class c on p.ClassId=c.Id

4.2 Left Join

  例如:查詢所有學生(參加及未參加考試的都算)及成績,這里涉及到學生表及成績表,題目要求參加及未參加考試的都要列出來,於是以學生表為基准,對成績表進行左連接:

select * from Student s
left join SC sc on s.S#=sc.S#

  可以通過運行結果圖,看到王二小這個童鞋沒有參加考試,也就沒有成績。

4.3 Right Join

  例如:要查詢出所有沒有參加考試(在成績表中不存在的學生)的學生的姓名。於是還是可以以學生表為基准,但是要對成績表進行右連接:

select * from Student s
right join SC sc on s.S#=sc.S#

4.4 Cross Join

  此種連接在實際應用中不算常見的,但卻是理論基礎,因為它代表了笛卡爾積。其實,所有連接方式都會先生成臨時笛卡爾積表,笛卡爾積是關系代數里的一個概念,表示第一個表的行數乘以第二個表的行數等於笛卡爾積結果集的大小。

select * from Student s
cross join SC sc

  這里假如Student表中有10行記錄,SC表中有20行記錄,那么兩表進行交叉連接笛卡爾積運算會得到10*20=200行記錄的結果集。

五、視圖

5.1 三個角度看視圖

  從用戶角度來看,一個視圖是從一個特定的角度來查看數據庫中的數據。

  從數據庫系統內部來看,一個視圖是由SELECT語句組成的查詢定義的虛擬表

  從數據庫系統內部來看,視圖是由一張或多張表中的數據組成的;從數據庫系統外部來看,視圖就如同一張表一樣,對表能夠進行的一般操作都可以應用於視圖,例如查詢,插入,修改,刪除操作等。

5.2 創建視圖

  例如,我們可以創建一個學生成績詳細信息視圖,對一個需要進行三表連接的查詢進行封裝:

create view vw_sc
as
select s.S#,s.Sname,c.Cname,sc.Score from Student s
join SC sc on s.S#=sc.S#
join Course c on sc.C#=c.C#

  然后,我們對vw_sc進行select查詢:

5.3 視圖的注意事項

  (1)視圖在操作上和數據表沒有什么區別,但兩者的差異是其本質是不同:數據表是實際存儲記錄的地方,然而視圖並不保存任何記錄

  (2)相同的數據表,根據不同用戶的不同需求,可以創建不同的視圖(不同的查詢語句)。

  (3)視圖的目的是方便查詢,所以 一般情況下不對視圖進行增改,不能刪

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM