個人學習4之--SQL中邏輯查詢處理的各個階段

  【個人學習4之--SQL中邏輯查詢處理的各個階段】 收藏
ps:首先鬱悶下 :前面在CSDN博客上直接寫,寫了一個多小時,按了個另存爲草稿,結果什麼都沒了。。鬱悶 這次在WORD上寫。
今天我來談下有關SQL中邏輯查詢處理的各個階段(自己搞的例子,不對的歡迎指正哦)
SQL不同於其他的編程語言的最大最大特徵有3個吧,

一個是它是面向集合的編程思想,第二個是三值邏輯(這個後面會說到),還有一個就是今天主要要說的查詢元素的邏輯處理次序。

請看一個基本查詢的邏輯過程:
(8)  SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1)  FROM <left_table>
(3)    <join_type> JOIN <right_table>
(2)      ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>
大家可以看到 這裏的運行步驟不是像一般的編程 一句句從上往下 它是跳動的 有活力的
這裏提前說下 每一步都會產生一個虛擬的表(也可能是遊標,下面會提到),作爲下一個步驟的輸入。大家最後看到的結果其實就是最後一個虛擬表了。
好了,下面我開始具體闡釋每個步驟:
測試環境:

--題目要求:求出學生最低科目成績不低於分且年齡在歲以上的學生姓名

create table #student(s# int,sname varchar(10),age int)

create table #study (s# int, c# char(1),score int)

insert #student

1,'xiaozhu',10 union all select

2,'xiaomao' ,9union all select

3,'xiaozhe' ,7union all select

4,'xiaophai',8 union all select

5,'xiaoduo',9

insert #study select

1,'A',99 union all select

1,'B',90 union all select

1,'C',99 union all select

2,'A',99 union all select

2,'b',99 union all select

2,'c',98 union all select

3,'A',99 union all select

3,'b',92 union all select

3,'c',91 union all select

3,'d',90 union all select

4,'A',88 union all select

4,'B',96

--SQL語句

select top 1 sname,MIN(score) as minsocre

from #student s left outer join #study sc

on s.s#=sc.s#

where age>7

group by sname

having MIN(score)>=90

order by minsocre

/*

sname      minsocre

---------- -----------

xiaozhu    90

*/

 

(1):執行笛卡爾積(CROSS JOIN )
大家這裏可能會覺得奇怪 明明上面的2後面只有一個表怎麼笛卡爾積呢?其實不是這樣的,它做的就是要將FROM最前面2表進行CROSS JOIN(因爲可能多表)
在我的例子裏就是#student 和#study表進行笛卡爾積操作(這個笛卡爾積相信大家都會),這裏就會出現第一個虛擬表,我們叫它VTB1
/*

/*


s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

1           xiaozhu    10          2           A    99

1           xiaozhu    10          2           b    99

1           xiaozhu    10          2           c    98

1           xiaozhu    10          3           A    99

1           xiaozhu    10          3           b    92

....

....

.....太多了,不佔空間

5           xiaoduo    9           2           b    99

5           xiaoduo    9           2           c    98

5           xiaoduo    9           3           A    99

5           xiaoduo    9           3           b    92

5           xiaoduo    9           3           c    91

5           xiaoduo    9           3           d    90

5           xiaoduo    9           4           A    88

5           xiaoduo    9           4           B    96

*/

 

(2)應用ON篩選器(聯接條件)
說到這裏,首先得說下 SQL用於查詢的有三個篩選器,分別是ON,where,having。
這裏的ON篩選器就是用VTB1作爲輸入,再利用ON後面的聯結條件進行篩選,生成VTB2。
sql編程第二特徵:三值邏輯:它其實就是所謂的TRUE FALSE UNKOWN
主要來說這個UNKOWN 它這邏輯值和NULL在一起就會”出事“。凡是NULL參與的比如 NULL>42, NULL-8>9 這些結果都是UNKOWN
UNKOWN 的邏輯結果在不同的環境下是被不同的方式處理的:如
在三個篩選器上,它是會被當做FALSE處理;
在CHECK約束上是會被當成TRUE來處理,check(col>8) 你插入的COL爲NULL,因爲NULL>8 的結果不就是UNKOWN ,所以是可以插入的。
在篩選器上的2個NULL比較將是FALSE 但是在UNIQUE約束,分組,排序上2個NULL是等價的。
好了,大概就這些吧 。我貼下VTB2的結果:

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

3           xiaozhe    7           3           A    99

3           xiaozhe    7           3           b    92

3           xiaozhe    7           3           c    91

3           xiaozhe    7           3           d    90

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

*/

(3)添加外部行(outer row)
這裏首先要知道一個地方:如果你的表沒有涉及到OUTER JOIN 那麼就不需要這步,這第三步就是爲外部聯接準備的。
我的測試環境下爲了說明這個地方,加了LEFT JOIN ,它這裏起到效果簡單來說就是拿回剛纔第二步損失的部分記錄,因爲你也看到了第五個學生'xiaoduo'
在虛擬表VTB2中沒出現,這裏完成後 它又回來了。vtb3

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

3           xiaozhe    7           3           A    99

3           xiaozhe    7           3           b    92

3           xiaozhe    7           3           c    91

3           xiaozhe    7           3           d    90

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

5           xiaoduo    9           null        null null

*/


PS: 如果你涉及到三表及三表以上操作,你就可以將這個VTB3表和你FROM後面的第三個表進行1-3步驟的重複操作,依次類推,最後再拿出一個虛擬表就OK

(4)應用WHERE篩選器
這裏看起來很簡單,只要將VTB3表 用WHERE 後面的條件過濾下得到VTB4表就可以。其實還是有很多知識在這的。
首先,在這我知道了爲什麼這裏不可以用聚合函數了,因爲數據在前面那幾步還沒被分組了,怎麼可以用聚合函數呢?也知道了不可以用那些你在select
後面定義的列別名,理由很簡單,你都沒操作那步呢。
再者,我們考慮一個問題:我們那個條件age>7 可以寫在ON後面麼?答案在這裏是否定的!
A:from #student s left outer join #study sc
on s.s#=sc.s#
where age>7

B:from #student s left outer join #study sc
on s.s#=sc.s# and s.age>7
這裏2種情況形成的虛擬表是這樣的
A:

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

5           xiaoduo    9           null        null null

*/


B:也就是VTB3

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

3           xiaozhe    7           3           A    99

3           xiaozhe    7           3           b    92

3           xiaozhe    7           3           c    91

3           xiaozhe    7           3           d    90

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

5           xiaoduo    9           null        null null

*/


大家可以看到,如果把AGE>7寫ON後面 這個出來的臨時表比寫WHERE後面多了那個年齡是7歲的xiaozhe,這顯然不是我們想要看到的。
原因就是因爲你這個ON後面的條件是在第二步就做完了,這個時候你過濾掉了7歲的xiaozhe 可是你第三步外聯接下 它就又可以回來的,
而如果寫where 後面就可以防止這種情況發生了。現在可以給出結論了,寫2個地方區別就在於ON過濾掉的記錄可以再恢復,但是WHERE過濾掉的就回不來了。
PS:我這說的這種情況也只是在有OUTER JOIN的情況下才會出現的,如果是INNER join之類的就不會出現差別,因爲它不存在第三步。
大家可以將語句換成

select  sname,MIN(score) as minsocre --這裏注意把TOP 1去掉不然看不出來了

from #student s left outer join #study sc

on s.s#=sc.s# and age>7

group by minsocre

having MIN(score)>=90

order by minsocre

/*

sname      minsocre

---------- -----------

xiaomao    98

xiaozhu    90


*/


爲了清楚說明,我再寫下這步產生的VTB4表:

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

5           xiaoduo    9           null        null null

*/


 


(5) 分組
這步就是分組,你確定你要分組的對象,它唯一的。然後根據這個基礎對象,把組分好。例子裏就是學生的姓名(假設沒出現重名的).
這裏的VTB5書上的意思就是其實由2部分組成:、
1是實際組構成的成組部分(xiaozhu,xiaomao,xiaophai,xiaoduo)這幾個基礎對象吧,
2就是本來那個VTB4傳下來的基礎數據。
這裏我們就可以看出一個東西:你在SELECT後面的列 要麼就包含在聚合函數裏面要麼就成爲GROUP BY 的基礎對象。
例如:你直接select sname,score 因爲你分組了 你這組只能返回關於這個組的一條信息。那麼對於XIAOZHU這組來說有三個SCORE,你讓它如何返回?
所以我們要用聚合函數來限定這個SCORE,如MIN(SCORE)。。。。。。
這裏再提一個小地方:原本在2000裏面你如果GROUP BY MIN(SCORE) 你在select 後面用SELECT MIN(SCORE)+1 as newscore 這樣是不可以的。
就是說:你不能對GROUP BY 的基礎對象做處理,除非他是表裏的列。但是2005裏面你可以放心使用我上面說的情況。
由於GROUP BY ALL是非標準的遺留物,所以不推薦,我也就不說了。。
VTB5:

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

------------------------------------------------------------

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

-------------------------------------------------------

4           xiaophai   8           4           A    88

4           xiaophai   8           4           B    96

-------------------------------------------------------

5           xiaoduo    9           null        null null

*/

(6) 應用CUBE 或者ROLLUP
我這稍微介紹一下ROLLUP 具體可以百度
用 ROLLUP 彙總數據,在生成包含小計和合計的報表時,ROLLUP 運算符很有用。
CUBE 和 ROLLUP 之間的區別在於:
CUBE 生成的結果集顯示了所選列中值的所有組合的聚合。
ROLLUP 生成的結果集顯示了所選列中值的某一層次結構的聚合。
例如,簡單表 Inventory 中包含:


Item                 Color                Quantity                  
-------------------- -------------------- --------------------------
Table                Blue                 124                       
Table                Red                  223                       
Chair                Blue                 101                       
Chair                Red                  210                       
下列查詢將生成小計報表:


 

SELECT CASE WHEN (GROUPING(Item) = 1) THEN 'ALL'

            ELSE ISNULL(Item, 'UNKNOWN')

       END AS Item,

       CASE WHEN (GROUPING(Color) = 1) THEN 'ALL'

            ELSE ISNULL(Color, 'UNKNOWN')

       END AS Color,

       SUM(Quantity) AS QtySum

FROM Inventory

GROUP BY Item, Color WITH ROLLUP


Item                 Color                QtySum                    

-------------------- -------------------- --------------------------

Chair                Blue                 101.00                    

Chair                Red                  210.00                    

Chair                ALL                  311.00                    

Table                Blue                 124.00                    

Table                Red                  223.00                    

Table                ALL                  347.00                    

ALL                  ALL                  658.00                    

 

 

有時,查詢優化器爲 ROLLUP 生成的執行計劃比爲 COMPUTE BY 生成的更爲高效。
 
(7) 應用HAVING篩選器
它是第一個用到分組以後的篩選器,也是最後一個,它在VTB5的基礎上(如果第六步有的話就是VTB6)經過條件再次篩選生成VTB7(這裏爲了和步驟一致,我就寫VTB7了)
例子中因爲xiaophai有門課是88分<90 xiaoduo因爲SCORE是NULL 所以都被排序掉

/*

s#          sname      age         s#          c#   score

----------- ---------- ----------- ----------- ---- -----------

1           xiaozhu    10          1           A    99

1           xiaozhu    10          1           B    90

1           xiaozhu    10          1           C    99

------------------------------------------------------------

2           xiaomao    9           2           A    99

2           xiaomao    9           2           b    99

2           xiaomao    9           2           c    98

*/


 


(8)處理SELECT列表
終於輪到了SELECT操作了,這步很簡單了,就是要注意分組後你的基本列只能用分組裏的基礎對象列了,其他列要用就得用聚合函數分組了,有重複的
列的時候要限定所屬表(這裏的s#)。這裏經過選擇產生VTB8
這裏有個SQL特性-同時操作:
就是表達式的別名不能用於SELECT列表的其他表達式。如 select c1+1 as c2 ,c2+1 as c3 應該改成 select c1+1 as c2 select c1+1+1 as c3
這個特性你可以理解爲操作都是一瞬間同時完成。否則你看下面的列子
UPDATE A
SET COL=(SELECT MAX(COL) FROM A)
如果你的更新不是一起完成,有先後順序,那你這裏就會出問題了。最大的COL隨時都在變可能,這跟你願意是不對老的。
這裏產生的VTB8
/*
sname      minsocre
---------- -----------
xiaomao    98
xiaozhu    90

*/

(9)應用DISTINCT 子句
這裏顧名思義就是從VTB8去重複的記錄得到VTB9

/*

sname      minsocre

---------- -----------

xiaomao    98

xiaozhu    90

*/


注意這裏如果你的代碼中出現了GROUP BY 那麼它是不會移除任何行的。而且DISTINCT會試效率降低很多,因爲它要遍歷表,不推薦使用

(10)應用ORDER BY 子句
這裏要將VTB9表裏的排序後,注意返回的是一個遊戲VC10.這裏的ORDER BY後面可以用SELECT後面的別名.
這裏有個地方要注意:如果你用了DISTINCT 那麼你的ORDER BY 只能訪問那個VTB9這個虛擬表。這的意思這麼講抽象,我舉例子:
select distinct sname,MIN(score) as minscore 我 後面ORDER BY 的時候只能order by sname 不能order by sname+'a'
如果你用 select distinct sname,MIN(score) as minscore OK 你可以用order by sname+'a'
PS:這步返回一個遊標而不是一個虛擬表原因是這樣:SQL是基於集合理論的。集合不會排序,只是包含數據。但是你排序後它是有特定物理
組織的行,ANSI就把這個對象叫做遊標.也是因爲OEDER BY返回的不是表,所以我們知道了使用了ORDER BY 的SQL語句不可以再試圖,子查詢,派生表,
CTE等表表達式中使用了。還有一般如果沒有特殊要求,別去排序,那是需要成本的。
話說回來,這次返回的遊標VC10:

/*

sname      minsocre

---------- -----------

xiaozhu    90

xiaomao    98


*/


(11)應用TOP選項
SQL2005裏面允許你在TOP後面加變量了,多好啊。(2000裏關於top +@n 的解決辦法看http://blog.csdn.net/feixianxxx/archive/2009/08/03/4405684.aspx)
通過限定,返回VTB11
這裏提個東西:如果你沒有指定ORDER BY 子句,帶有TOP的查詢不確定。它返回的行正好是SQL物理上最先訪問的行。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/feixianxxx/archive/2009/10/18/4694354.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章