當前位置:首頁 » 編程語言 » sql子查詢的執行順序
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

sql子查詢的執行順序

發布時間: 2022-12-14 13:35:34

『壹』 sql的執行順序 與 游標

最近項目中使用了很多大sql,在編碼時sql經常出現執行錯誤,記錄下sql的執行順序,在編碼時就考慮全面,省的之後還要繼續花時間進行試驗調試。

(8)SELECT (9)DISTINCT (11)<Top Num> <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>

1.FROM: 對FROM子句中的前兩個表執行笛卡爾積(Cartesian proct)(交叉聯接),生成虛擬表VT1

2.ON: 對VT1應用ON篩選器。只有那些使為真的行才被插入VT2。

3.OUTER(JOIN): 如 果指定了OUTER JOIN(相對於CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部聯接把左表標記為保留表,右外部聯接把右表標記為保留表,完全外部聯接把兩個表都標記為保留表)中未找到匹配的行將作為外部行添加到 VT2,生成VT3.如果FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重復執行步驟1到步驟3,直到處理完所有的表為止。

4.WHERE: 對VT3應用WHERE篩選器。只有使為true的行才被插入VT4.

5.GROUP BY: 按GROUP BY子句中的列列表對VT4中的行分組,生成VT5.

6.CUBE|ROLLUP: 把超組(Suppergroups)插入VT5,生成VT6.

7.HAVING: 對VT6應用HAVING篩選器。只有使為true的組才會被插入VT7.

8.SELECT: 處理SELECT列表,產生VT8.

9.DISTINCT: 將重復的行從VT8中移除,產生VT9.

10.ORDER BY: 將VT9中的行按ORDER BY 子句中的列列表排序,生成游標(VC10).

11.TOP: 從VC10的開始處選擇指定數量或比例的行,生成表VT11,並返回調用者。

註:步驟10,按ORDER BY子句中的列列表排序上步返回的行,返回遊標VC10.這一步是第一步也是唯一一步可以使用SELECT列表中的列別名的步驟。這一步不同於其它步驟的 是,它不返回有效的表,而是返回一個游標。SQL是基於集合理論的。集合不會預先對它的行排序,它只是成員的邏輯集合,成員的順序無關緊要。對表進行排序 的查詢可以返回一個對象,包含按特定物理順序組織的行。ANSI把這種對象稱為游標。理解這一步是正確理解SQL的基礎。

因為這一步不返回表(而是返回遊標),使用了ORDER BY子句的查詢不能用作表表達式。表表達式包括:視圖、內聯表值函數、子查詢、派生表和共用表達式。它的結果必須返回給期望得到物理記錄的客戶端應用程序。例如,下面的派生表查詢無效,並產生一個錯誤:select * from(select orderid,customerid from orders order by orderid) as d

在SQL中,表表達式中不允許使用帶有ORDER BY子句的查詢,而在T—SQL中卻有一個例外(應用TOP選項)。所以要記住,不要為表中的行假設任何特定的順序。換句話說,除非你確定要有序行,否則不要指定ORDER BY 子句。排序是需要成本的,SQL Server需要執行有序索引掃描或使用排序運行符。

參考:

https://www.cnblogs.com/knowledgesea/p/3699851.html

https://www.cnblogs.com/

『貳』 SQL 子查詢

所謂 SQL 子查詢就是嵌套在其他查詢中的查詢。子查詢通常用於 WHERE 子句的 IN 操作符中進行過濾,以及用來填充計算列。下面我們從這兩種使用場景展開學習。

本節涉及到關系表如下:

最上方的訂單表 Orders 存儲了訂單 ID 、訂單日期以及顧客 ID ;具體的訂單信息存儲在 OrderItems 表中,通過 order_num 進行關聯;具體的顧客信息存儲在顧客表 Customers 中,通過 cust_id 欄位進行關聯。

下面,假設我們需要檢索出購買了 RGA01 產品的所有顧客信息,應該怎麼做呢?

首先,我們只列出步驟:

接下來,我們來完成第一步:

包含了產品 RGAN01 的訂單編號:

有了訂單編號,就可以從訂單表檢索出顧客 ID 了:

運行結果:

接下來,我們來合並上面的兩步:把第一個查詢變為子查詢,放在 WHERE 語句的 IN 操作符之後:

運行結果和上述第二步獲得的結果是一樣的:

SELECT 語句中子查詢從內向外處理:首先執行 SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01' 子查詢,將返回的訂單號作為 IN 操作符的參數,執行外部查詢: SELECT cust_id FROM Orders WHERE order_num IN ( 20007, 20008 ); 。

最後,我們來看第三步,根據顧客 ID 檢索出顧客相關信息。

運行結果:

同理,我們可以將上述的 WHERE 子句替換為子查詢:

至此,我們的檢索任務就完成了:查詢購買了 RGAN01 商品的所有顧客姓名及聯系方式。

子查詢的另一個使用場景為創建計算欄位。同樣,我們以一個案例來學習:檢索每個顧客的訂單的總數。

顧客表與訂單表是一對多的關系,即一個顧客對應多個訂單,但一個訂單只對應一個顧客。

如下的 SQL 檢索出顧客 1000000001 的訂單數:

要對每個顧客的訂單計數,應該將其作為子查詢:

運行結果:

上述 SQL Customers 表中每個顧客返回三列: cust_name cust_state 和 orders 。其中 orders 為子查詢創建的計算欄位,該子查詢對檢索出的每個顧客都執行一次,一共執行了 6 次子查詢。

在子查詢的條件中,我們使用了表的完全限定列名 Orders.cust_id = Customers.cust_id ,這是因為 Orders 表和 Customers 表中包含了欄位名名稱相同的列。

本節我們學習了在 SELECT 語句中使用子查詢的兩種用法:將子查詢應用於 WHERE 子句的 IN 操作符中,進行條件過濾,以及用子查詢創建計算欄位。

子查詢的檢索效率不夠理想,下一篇文章中小魚將和大家展開聯結表的學習~ 連接表 是數據檢索的精華和重點,我們拭目以待吧!

『叄』 資料庫 SQL語句 子查詢執行過程

子查詢執行過程,可以用以下例子來說明:

語句如下:

select*fromscorewheresidin(selectsidfromstudentwhere班級='一班')

在sql語句中,資料庫先執行的是括弧中的部分,得出student表中一班學生的sid,然後再在score表中選出sid為一班id的哪些學生的詳細內容。

『肆』 一文講懂SQL子查詢

大家好,我是寧一。


今天講解SQL教程第18課:子查詢。


SQL語句可以嵌套,最常見的就是查詢語句的嵌套。


基本語法:



我們一般稱外面嵌套的語句為主查詢,裡面被嵌套的語句為子查詢,有時也會叫外查詢、內查詢,大家知道意思就好。


子查詢要用括弧括起來。子查詢不僅可以放在WHERE的後面,還可以放在SELECT、FROM的後面,我們一個個來講解。


1、子查詢+WHERE子句


SQL執行時,會先執行括弧內的子查詢,子查詢最常與WHERE子句結合使用。子查詢的結果作為WHERE子句的篩選條件,完成更復雜的數據檢索。


實例: 在Students表中,找出所有在"寧一"後面出生的學生。



實例解析: 需要先確定"寧一"的生日,再將生日作為WHERE篩選條件,得到最終數據。


第一步:找到"寧一"的生日




第二步:將生日作為WHERE篩選條件,得到最終數據,子查詢語句要用括弧括起來。






SELECT語句的子查詢經常與聚合函數結合使用。因為我們使用聚合函數的時候,記錄會合成一條,其它數據細節就不能顯示了。


比如: 我們想要查看學生表中所有的學生姓名、學生生日、學生的最大生日。


示例結果:



錯誤寫法:



像上面這樣寫是會報錯的,因為聚合函數與其他表中的列(Sname,Sage),同時放在SELECT的後面。需要用GROUP BY語句將這些表中的列(Sname,Sage)分組。


上面的語句後面加上 GROUP BY Sname,Sage 就可以了。


但是這樣寫,會將每組的數據聚合成1條數據,比如每組有3條數據,使用聚合函數MAX()+GROUP BY,最終每組只會顯示1條最大值的數據。


我們需要展現Students表中所有的學生,這樣寫不能滿足我們的需求。


正確寫法: 結合子查詢來實現。




子查詢與FROM子句結合使用,子查詢結果被當成了一個「表」,可以用SELECT語句做進一步的篩查。


比如:我們先寫一個SELECT查詢語句




將上面的查詢語句放在FROM的後面,則上面查詢到的結果,就會被當成一個「表」。



這里有一個特別要注意的地方,放在FROM後面的子查詢,必須要加別名。



復雜的子查詢再嵌套進 FROM 里會讓整個查詢看起來過於復雜,我們一般會將子查詢結果儲存為視圖,然後再直接使用視圖作為來源表,視圖會SQL高階課程中詳細講解。


其實子查詢就是查詢語句嵌套,沒有什麼新的東西,只是多了一個層級,由內向外地一層層梳理就會很清楚了。


作業: 結合Students表,從Teachers表中找出當班主任的老師(通過子查詢實現)。



作業解析: 先從Students表中,找出所有班主任的Tid並去重,將查詢結果作為篩選條件,放在WHERE語句中。



『伍』 sql查詢語句的各個命令執行的標准順序是什麼為什麼

查詢語句是sql語句中使用最多的操作,也涉及到非常多的命令。比如where過濾,group

by分組,order by 排序 limit取值 having等。雖然多,但是各個命令執行的時候卻是有順序的,順序如下:
select *
from 表名
①-- where 條件1
②-- group by 依據列
③-- having 條件2
④-- order by 依據列
⑤-- limit 0,1

為什麼是這么個順序,原因:
limit取值永遠是最後一個.
如果你要order by排序,前提是要首先得到一個查詢結果.
查詢結果中的三個關鍵詞,where總是是放在表名的後面,而havin過濾永遠是放在group後面,所以就有了這么個順序.如果不遵循順序,就會出現錯誤。

是不是這樣,我們可以用下面的建表語句驗證下.
drop table if exists students;
create table students (
studentNo varchar(10) primary key,
name varchar(10),
sex varchar(1),
hometown varchar(20),
age tinyint(4),
class varchar(10),
card varchar(20)
);
insert into students values
('001', '王昭君', '女', '北京', '20', '1班', '340322199001247654'),
('002', '諸葛亮', '男', '上海', '18', '2班', '340322199002242354'),
('003', '張飛', '男', '南京', '24', '3班', '340322199003247654'),
('004', '白起', '男', '安徽', '22', '4班', '340322199005247654'),
('005', '大喬', '女', '天津', '19', '3班', '340322199004247654'),
('006', '孫尚香', '女', '河北', '18', '1班', '340322199006247654'),
('007', '百里玄策', '男', '山西', '20', '2班', '340322199007247654'),
('008', '小喬', '女', '河南', '15', '3班', null),
('009', '百里守約', '男', '湖南', '21', '1班', ''),
('010', '妲己', '女', '廣東', '26', '2班', '340322199607247654'),
('011', '李白', '男', '北京', '30', '4班', '340322199005267754'),
('012', '孫臏', '男', '新疆', '26', '3班', '340322199000297655')

查詢該表中除1班外,所有其他班級的最大年齡,最小年齡,並且按照班號進行降序排列(過濾掉2班,只顯示最前面的一條信息)

那麼sql語句就是: select class,max(age),min(age) from students where class !='1班' group by class having class !='2班' order by class desc limit 1

在這條語句中,新人非常容易犯的錯誤 就是根據題意,將having放在order by 後面導致錯誤。原因在於order by應該放在hving的後面。 還有其他疑問的話建議你去搜一下黑馬程序員,在IT業內算是比較有實力的機構,裡面有線上和線下的課程,還有免費的視頻,每天看一點學一下還是比較有用的。不過,如果真的是想入行或者進階的話,花一段時間高專注度的學習是非常有必要的,投資自己報課試一下吧。

『陸』 SQL語句執行流程與順序原理解析

SQL語句執行流程與順序原理解析
Oracle語句執行流程
第一步:客戶端把語句發給伺服器端執行
當我們在客戶端執行SQL語句時,客戶端會把這條SQL語句發送給伺服器端,讓伺服器端的進程來處理這語句。也就是說,Oracle 客戶端是不會做任何的操作,他的主要任務就是把客戶端產生的一些SQL語句發送給伺服器端。伺服器進程從用戶進程把信息接收到後, 在PGA 中就要此進程分配所需內存,存儲相關的信息,如:在會話內存存儲相關的登錄信息等。
雖然在客戶端也有一個資料庫進程,但是,這個進程的作用跟伺服器上的進程作用是不相同的,伺服器上的資料庫進程才會對SQL 語句進行相關的處理。不過,有個問題需要說明,就是客戶端的進程跟伺服器的進程是一一對應的。也就是說,在客戶端連接上伺服器後,在客戶端與伺服器端都會形成一個進程,客戶端上的我們叫做客戶端進程,而伺服器上的我們叫做伺服器進程。
第二步:語句解析
當客戶端把SQL語句傳送到伺服器後,伺服器進程會對該語句進行解析。這個解析的工作是在伺服器端所進行的,解析動作又可分為很多小動作。
1)查詢高速緩存(library cache)
伺服器進程在接到客戶端傳送過來的SQL語句時,不會直接去資料庫查詢。伺服器進程把這個SQL語句的字元轉化為ASCII等效數字碼,接著這個ASCII碼被傳遞給一個HASH函數,並返回一個hash值,然後伺服器進程將到shared pool中的library cache(高速緩存)中去查找是否存在相同的hash值。如果存在,伺服器進程將使用這條語句已高速緩存在SHARED POOL的library cache中的已分析過的版本來執行,省去後續的解析工作,這便是軟解析。若調整緩存中不存在,則需要進行後面的步驟,這便是硬解析。硬解析通常是昂貴的操作,大約占整個SQL執行的70%左右的時間,硬解析會生成執行樹,執行計劃,等等。
所以,採用高速數據緩存的話,可以提高SQL 語句的查詢效率。其原因有兩方面:一方面是從內存中讀取數據要比從硬碟中的數據文件中讀取數據效率要高,另一方面也是因為避免語句解析而節省了時間。
不過這里要注意一點,這個數據緩存跟有些客戶端軟體的數據緩存是兩碼事。有些客戶端軟體為了提高查詢效率,會在應用軟體的客戶端設置數據緩存。由於這些數據緩存的存在,可以提高客戶端應用軟體的查詢效率。但是,若其他人在伺服器進行了相關的修改,由於應用軟體數據緩存的存在,導致修改的數據不能及時反映到客戶端上。從這也可以看出,應用軟體的數據緩存跟資料庫伺服器的高速數據緩存不是一碼事。
2)語句合法性檢查(data dict cache)
當在高速緩存中找不到對應的SQL語句時,則伺服器進程就會開始檢查這條語句的合法性。這里主要是對SQL語句的語法進行檢查,看看其是否合乎語法規則。如果伺服器進程認為這條SQL語句不符合語法規則的時候,就會把這個錯誤信息反饋給客戶端。在這個語法檢查的過程中,不會對SQL語句中所包含的表名、列名等等進行檢查,只是檢查語法。
3)語言含義檢查(data dict cache)
若SQL 語句符合語法上的定義的話,則伺服器進程接下去會對語句中涉及的表、索引、視圖等對象進行解析,並對照數據字典檢查這些對象的名稱以及相關結構,看看這些欄位、表、視圖等是否在資料庫中。如果表名與列名不準確的話,則資料庫會就會反饋錯誤信息給客戶端。
所以,有時候我們寫select語句的時候,若語法與表名或者列名同時寫錯的話,則系統是先提示說語法錯誤,等到語法完全正確後再提示說列名或表名錯誤。
4)獲得對象解析鎖(control structer)
當語法、語義都正確後,系統就會對我們需要查詢的對象加鎖。這主要是為了保障數據的一致性,防止我們在查詢的過程中,其他用戶對這個對象的結構發生改變。
5)數據訪問許可權的核對(data dict cache)
當語法、語義通過檢查之後,客戶端還不一定能夠取得數據,伺服器進程還會檢查連接用戶是否有這個數據訪問的許可權。若用戶不具有數據訪問許可權的話,則客戶端就不能夠取得這些數據。要注意的是資料庫伺服器進程先檢查語法與語義,然後才會檢查訪問許可權。
6)確定最佳執行計劃
當語法與語義都沒有問題許可權也匹配,伺服器進程還是不會直接對資料庫文件進行查詢。伺服器進程會根據一定的規則,對這條語句進行優化。在執行計劃開發之前會有一步查詢轉換,如:視圖合並、子查詢解嵌套、謂語前推及物化視圖重寫查詢等。為了確定採用哪個執行計劃,Oracle還需要收集統計信息確定表的訪問聯結方法等,最終確定可能的最低成本的執行計劃。
不過要注意,這個優化是有限的。一般在應用軟體開發的過程中,需要對資料庫的sql語句進行優化,這個優化的作用要大大地大於伺服器進程的自我優化。
當伺服器進程的優化器確定這條查詢語句的最佳執行計劃後, 就會將這條SQL語句與執行計劃保存到數據高速緩存(library cache)。如此,等以後還有這個查詢時,就會省略以上的語法、語義與許可權檢查的步驟,而直接執行SQL語句,提高SQL語句處理效率。
第三步:綁定變數賦值
如果SQL語句中使用了綁定變數,掃描綁定變數的聲明,給綁定變數賦值,將變數值帶入執行計劃。若在解析的第一個步驟,SQL在高速緩沖中存在,則直接跳到該步驟。
第四步:語句執行
語句解析只是對SQL語句的語法進行解析,以確保伺服器能夠知道這條語句到底表達的是什麼意思。等到語句解析完成之後,資料庫伺服器進程才會真正的執行這條SQL語句。
對於SELECT語句:
1)首先伺服器進程要判斷所需數據是否在db buffer存在,如果存在且可用,則直接獲取該數據而不是從資料庫文件中去查詢數據,同時根據LRU 演算法增加其訪問計數;
2)若數據不在緩沖區中,則伺服器進程將從資料庫文件中查詢相關數據,並把這些數據放入到數據緩沖區中(buffer cache)。
其中,若數據存在於db buffer,其可用性檢查方式為:查看db buffer塊的頭部是否有事務,如果有事務,則從回滾段中讀取數據;如果沒有事務,則比較select的scn和db buffer塊頭部的scn,如果前者小於後者,仍然要從回滾段中讀取數據;如果前者大於後者,說明這是一非臟緩存,可以直接讀取這個db buffer塊的中內容。
對於DML語句(insert、delete、update):
1)檢查所需的資料庫是否已經被讀取到緩沖區緩存中。如果已經存在緩沖區緩存,則直接執行步驟3;
2)若所需的資料庫並不在緩沖區緩存中,則伺服器將數據塊從數據文件讀取到緩沖區緩存中;
3)對想要修改的表取得的數據行鎖定(Row Exclusive Lock),之後對所需要修改的數據行取得獨占鎖;
4)將數據的Redo記錄復制到redo log buffer;
5)產生數據修改的undo數據;
6)修改db buffer;
7)dbwr將修改寫入數據文件;
其中,第2步,伺服器將數據從數據文件讀取到db buffer經經歷以下步驟:
1)首先伺服器進程將在表頭部請求TM鎖(保證此事務執行過程其他用戶不能修改表的結構),如果成功加TM鎖,再請求一些行級鎖(TX鎖),如果TM、TX鎖都成功加鎖,那麼才開始從數據文件讀數據。
2)在讀數據之前,要先為讀取的文件准備好buffer空間。伺服器進程需要掃描LRU list尋找free db buffer,掃描的過程中,伺服器進程會把發現的所有已經被修改過的db buffer注冊到dirty list中。如果free db buffer及非臟數據塊緩沖區不足時,會觸發dbwr將dirty buffer中指向的緩沖塊寫入數據文件,並且清洗掉這些緩沖區來騰出空間緩沖新讀入的數據。
3)找到了足夠的空閑buffer,伺服器進程將從數據文件中讀入這些行所在的每一個數據塊(db block)(DB BLOCK是ORACLE的最小操作單元,即使你想要的數據只是DB BLOCK中很多行中的一行或幾行,ORACLE也會把這個DB BLOCK中的所有行都讀入Oracle DB BUFFER中)放入db buffer的空閑的區域或者覆蓋已被擠出LRU list的非臟數據塊緩沖區,並且排列在LRU列表的頭部,也就是在數據塊放入db buffer之前也是要先申請db buffer中的鎖存器,成功加鎖後,才能讀數據到db buffer。
若數據塊已經存在於db buffer cache(有時也稱db buffer或db cache),即使在db buffer中找到一個沒有事務,而且SCN比自己小的非臟緩存數據塊,伺服器進程仍然要到表的頭部對這條記錄申請加鎖,加鎖成功才能進行後續動作,如果不成功,則要等待前面的進程解鎖後才能進行動作(這個時候阻塞是tx鎖阻塞)。
在記redo日誌時,其具體步驟如下:
1)數據被讀入到db buffer後,伺服器進程將該語句所影響的並被讀入db buffer中的這些行數據的rowid及要更新的原值和新值及scn等信息從PGA逐條的寫入redo log buffer中。在寫入redo log buffer之前也要事先請求redo log buffer的鎖存器,成功加鎖後才開始寫入。
2)當寫入達到redo log buffer大小的三分之一或寫入量達到1M或超過三秒後或發生檢查點時或者dbwr之前發生,都會觸發lgwr進程把redo log buffer的數據寫入磁碟上的redo file文件中(這個時候會產生log file sync等待事件)。
3)已經被寫入redo file的redo log buffer所持有的鎖存器會被釋放,並可被後來的寫入信息覆蓋,redo log buffer是循環使用的。Redo file也是循環使用的,當一個redo file寫滿後,lgwr進程會自動切換到下一redo file(這個時候可能出現log file switch(check point complete)等待事件)。如果是歸檔模式,歸檔進程還要將前一個寫滿的redo file文件的內容寫到歸檔日誌文件中(這個時候可能出現log file switch(archiving needed)。
在為事務建立undo信息時,其具體步驟如下:
1)在完成本事務所有相關的redo log buffer之後,伺服器進程開始改寫這個db buffer的塊頭部事務列表並寫入scn(一開始scn是寫在redo log buffer中的,並未寫在db buffer)。
2)然後包含這個塊的頭部事務列表及scn信息的數據副本放入回滾段中,將這時回滾段中的信息稱為數據塊的「前映像」,這個「前映像」用於以後的回滾、恢復和一致性讀。(回滾段可以存儲在專門的回滾表空間中,這個表空間由一個或多個物理文件組成,並專用於回滾表空間,回滾段也可在其它表空間中的數據文件中開辟)。
在修改信息寫入數據文件時,其具體步驟如下:
1)改寫db buffer塊的數據內容,並在塊的頭部寫入回滾段的地址。
2)將db buffer指針放入dirty list。如果一個行數據多次update而未commit,則在回滾段中將會有多個「前映像」,除了第一個「前映像」含有scn信息外,其他每個"前映像"的頭部都有scn信息和"前前映像"回滾段地址。一個update只對應一個scn,然後伺服器進程將在dirty list中建立一條指向此db buffer塊的指針(方便dbwr進程可以找到dirty list的db buffer數據塊並寫入數據文件中)。接著伺服器進程會從數據文件中繼續讀入第二個數據塊,重復前一數據塊的動作,數據塊的讀入、記日誌、建立回滾段、修改數據塊、放入dirty list。
3)當dirty queue的長度達到閥值(一般是25%),伺服器進程將通知dbwr把臟數據寫出,就是釋放db buffer上的鎖存器,騰出更多的free db buffer。前面一直都是在說明oracle一次讀一個數據塊,其實oracle可以一次讀入多個數據塊(db_file_multiblock_read_count來設置一次讀入塊的個數)
當執行commit時,具體步驟如下:
1)commit觸發lgwr進程,但不強制dbwr立即釋放所有相應db buffer塊的鎖。也就是說有可能雖然已經commit了,但在隨後的一段時間內dbwr還在寫這條sql語句所涉及的數據塊。表頭部的行鎖並不在commit之後立即釋放,而是要等dbwr進程完成之後才釋放,這就可能會出現一個用戶請求另一用戶已經commit的資源不成功的現象。
2)從Commit和dbwr進程結束之間的時間很短,如果恰巧在commit之後,dbwr未結束之前斷電,因為commit之後的數據已經屬於數據文件的內容,但這部分文件沒有完全寫入到數據文件中。所以需要前滾。由於commit已經觸發lgwr,這些所有未來得及寫入數據文件的更改會在實例重啟後,由smon進程根據重做日誌文件來前滾,完成之前commit未完成的工作(即把更改寫入數據文件)。
3)如果未commit就斷電了,因為數據已經在db buffer更改了,沒有commit,說明這部分數據不屬於數據文件。由於dbwr之前觸發lgwr也就是只要數據更改,(肯定要先有log)所有dbwr在數據文件上的修改都會被先一步記入重做日誌文件,實例重啟後,SMON進程再根據重做日誌文件來回滾。
其實smon的前滾回滾是根據檢查點來完成的,當一個全部檢查點發生的時候,首先讓LGWR進程將redologbuffer中的所有緩沖(包含未提交的重做信息)寫入重做日誌文件,然後讓dbwr進程將dbbuffer已提交的緩沖寫入數據文件(不強制寫未提交的)。然後更新控制文件和數據文件頭部的SCN,表明當前資料庫是一致的,在相鄰的兩個檢查點之間有很多事務,有提交和未提交的。
當執行rollback時,具體步驟如下:
伺服器進程會根據數據文件塊和db buffer中塊的頭部的事務列表和SCN以及回滾段地址找到回滾段中相應的修改前的副本,並且用這些原值來還原當前數據文件中已修改但未提交的改變。如果有多個」前映像「,伺服器進程會在一個「前映像」的頭部找到「前前映像」的回滾段地址,一直找到同一事務下的最早的一個「前映像」為止。一旦發出了commit,用戶就不能rollback,這使得commit後dbwr進程還沒有全部完成的後續動作得到了保障。
第五步:提取數據
當語句執行完成之後,查詢到的數據還是在伺服器進程中,還沒有被傳送到客戶端的用戶進程。所以,在伺服器端的進程中,有一個專門負責數據提取的一段代碼。他的作用就是把查詢到的數據結果返回給用戶端進程,從而完成整個查詢動作。
從這整個查詢處理過程中,我們在資料庫開發或者應用軟體開發過程中,需要注意以下幾點:
一是要了解資料庫緩存跟應用軟體緩存是兩碼事情。資料庫緩存只有在資料庫伺服器端才存在,在客戶端是不存在的。只有如此,才能夠保證資料庫緩存中的內容跟資料庫文件的內容一致。才能夠根據相關的規則,防止數據臟讀、錯讀的發生。而應用軟體所涉及的數據緩存,由於跟資料庫緩存不是一碼事情,所以,應用軟體的數據緩存雖然可以提高數據的查詢效率,但是,卻打破了數據一致性的要求,有時候會發生臟讀、錯讀等情況的發生。所以,有時候,在應用軟體上有專門一個功能,用來在必要的時候清除數據緩存。不過,這個數據緩存的清除,也只是清除本機上的數據緩存,或者說,只是清除這個應用程序的數據緩存,而不會清除資料庫的數據緩存。
二是絕大部分SQL語句都是按照這個處理過程處理的。我們DBA或者基於Oracle資料庫的開發人員了解這些語句的處理過程,對於我們進行涉及到SQL語句的開發與調試,是非常有幫助的。有時候,掌握這些處理原則,可以減少我們排錯的時間。特別要注意,資料庫是把數據查詢許可權的審查放在語法語義的後面進行檢查的。所以,有時會若光用資料庫的許可權控制原則,可能還不能滿足應用軟體許可權控制的需要。此時,就需要應用軟體的前台設置,實現許可權管理的要求。而且,有時應用資料庫的許可權管理,也有點顯得繁瑣,會增加伺服器處理的工作量。因此,對於記錄、欄位等的查詢許可權控制,大部分程序涉及人員喜歡在應用程序中實現,而不是在資料庫上實現。
Oracle SQL語句執行順序
(8)SELECT (9) DISTINCT (11) <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>
1)FROM:對FROM子句中的表執行笛卡爾積(交叉聯接),生成虛擬表VT1。
2)ON:對VT1應用ON篩選器,只有那些使為真才被插入到TV2。
3)OUTER (JOIN):如果指定了OUTER JOIN(相對於CROSS JOIN或INNER JOIN),保留表中未找到匹配的行將作為外部行添加到VT2,生成TV3。如果FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重復執行步驟1到步驟3,直到處理完所有的表位置。
4)WHERE:對TV3應用WHERE篩選器,只有使為true的行才插入TV4。
5)GROUP BY:按GROUP BY子句中的列列表對TV4中的行進行分組,生成TV5。
6)CUTE|ROLLUP:把超組插入VT5,生成VT6。
7)HAVING:對VT6應用HAVING篩選器,只有使為true的組插入到VT7。
8)SELECT:處理SELECT列表,產生VT8。
9)DISTINCT:將重復的行從VT8中刪除,產品VT9。
10)ORDER BY:將VT9中的行按ORDER BY子句中的列列表順序,生成一個游標(VC10),生成表TV11,並返回給調用者。
以上每個步驟都會產生一個虛擬表,該虛擬表被用作下一個步驟的輸入。這些虛擬表對調用者(客戶端應用程序或者外部查詢)不可用。只有最後一步生成的表才會會給調用者。如果沒有在查詢中指定某一個子句,將跳過相應的步驟。

『柒』 oracle SQL子查詢與主查詢誰先執行

毫無疑問是主查詢先。
例如
select * from A
WHERE id < 5
AND ID IN (SELECT ID FROM B WHERE xxxx)
這里由於是and連接,不存在優先順序問題。所以查詢的時候先全表掃描 id < 5的,然後再多一個條件,id in 子查詢。此時,先去查詢子查詢。然後再回到主查詢。

從這個例子可以看出每次掃描都要做子查詢掃描,從而導致了效率低下。這也是為什麼不推薦子查詢的根本原因。

『捌』 sql語句執行順序之group by、order by

1、先執行group by後執行order by,如果相同id的記錄只獲取id大的一條記錄,使用子查詢(先排序後分組):

select * from (select * from table1 order by id desc limit 9999) a group by type_id;

PS:group by需要和limit配合使用,不使用limit語句會自動被優化掉group by無效。

2、欄位值為0的記錄不分組,欄位值大於0的記錄進行分組:

方法1:使用union all

SELECT * FROM `table1` WHERE name='0' UNION ALL SELECT * FROM `table1` WHERE name!='0' group by name;

方法2:使用case when :select的時候判斷id是否等於0,等於0的話則賦值,然後再使用group by分組

select * from (select o.add_time,og.id,(CASE WHEN og.proct_id<1 THEN o.add_time ELSE og.proct_id END) as proct_id from order as o left join order_goods as og on o.order_id=og.order_id order by o.add_time desc limit 9999) table1 group by proct_id order by add_time desc

拓展:(使用上面sql)如果proct_id不為空,需要加上判斷只獲取開啟展示狀態的proct數據:

select * from (select o.add_time,og.id,(CASE WHEN og.proct_id>1 THEN (select id from proct where proct.id=og.proct_id and proct.is_show=1) ELSE o.add_time END) as proct_id from order as o left join order_goods as og on o.order_id=og.order_id order by o.add_time desc limit 9999) table1  where proct_id is not null group by proct_id order by add_time desc

方法3:使用isfull()函數 ,思路和方法2一樣,都是判斷欄位值是否為空,若是空值先賦一個臨時值後分組

需要注意的是,isfull只能用於判斷是否為null,若值是0無效(見圖3 圖4)

『玖』 SQL必知必會(二)函數、子查詢

#文本處理函數

UPPER()     --大寫

LOWER()    --小寫

SOUNDEX()   --讀音類似

LENGTH()    --字元串長度

#日期和時間處理函數

SELECT order_code

FROM order_table

WHERE DATEPART(yy,order_date)=2012

#to_char()函數提取日期成分,MySQL可用year()函數提取年份

SELECT order_num

FROM order_table

WHERE to_number(to_char(order_date,'YYYY'))=2012;    --to_char提取日期成分to_number轉化為數值

SELECT order_num

FROM order_table

WHERE order_date BETWEEN to_date('01-01-2012') AND to_date('12-31--2012');    --字元串轉日期

#數值處理函數

ABS()    --絕對值

COS()    --餘弦

EXP()    --指數值

PI()   --圓周率

SIN()    --正弦

SQRT()    --平方根

TAN()    --正切

#平均數AVG()

SELECT AVG(a) AS avg_a

FROM tableA

WHERE a='DLL01';

#計數

SELECT COUNT(*)  AS num_cust              --對所有行計數,不管是否NULL值

FROM tableA;

SELECT COUNT(a) AS num_a          --指定了列名,會忽略NULL值

FROM tableA;

#最值

MAX()和MIN(),忽略NULL值

#總值SUM(),忽略NULL值

SELECT SUM(price*quantity) AS total_price

FROM tableA

WHERE order_code=20008

#以上聚集函數只包含不同值

SELECT AVG(DISTINCT order_price) AS avg_price

FROM tableA

WHERE id='DLL01';

兩個子句:GROUP BY 和 HAVING

#GROUP BY創建分組HAVING過濾分組

SELECT vend_id, COUNT(*) AS num_prods

FROM tableA

WHERE price>=4

GROUP BY vend_id

HAVING COUNT(*)>=2;   

ORDER BY price    --GROUP BY在WHERE之後在ORDER   BY之前

SELECT

FROM

WHERE

GROUP BY

HAVING

ORDER BY

#子查詢順序為從內而外

SELECT order_num 

FROM tableA

WHERE id='DLL01';

(輸出20007和20008)

SELECT id

FROM tableB

WHERE order_num IN(20007,20008);

#合並為子查詢。只能查詢單個列

SELECT id

FROM tableB

WHERE order_num IN (SELECT order_num FROM tableA WHERE id='DLL01')

SELECT  cust_name,

                cust_state

                (SELECT COUNT(*) 

                 FROM Orders

                WHERE Orders.cust_id=Customers.cust_id) AS orders

FROM Customers

ORDER BY cust_name;

#Orders.cust_id=Customers.cust_id完全限定列名,在兩張表中名字相同列拿出來比較,防止歧義

『拾』 SQL執行順序

查詢語句中select from where group by having order by的執行順序

1.查詢中用到的關鍵詞主要包含六個,並且他們的順序依次為 

select--from--where--group by--having--order by 

其中select和from是必須的,其他關鍵詞是可選的,這六個關鍵詞的執行順序 

與sql語句的書寫順序並不是一樣的,而是按照下面的順序來執行 

from--where--group by--having--select--order by, 

from:需要從哪個數據表檢索數據 

where:過濾表中數據的條件 

group by:如何將上面過濾出的數據分組 

having:對上面已經分組的數據進行過濾的條件  

select:查看結果集中的哪個列,或列的計算結果 

order by :按照什麼樣的順序來查看返回的數據

2.from後面的表關聯,是自右向左解析的 

而where條件的解析順序是自下而上的。 

也就是說,在寫SQL文的時候,盡量把數據量大的表放在最右邊來進行關聯, 

而把能篩選出大量數據的條件放在where語句的最下面。

SQL Select語句完整的 執行順序 【從DBMS使用者角度】:

1、from子句組裝來自不同數據源的數據;

2、where子句基於指定的條件對記錄行進行篩選;

3、group by子句將數據劃分為多個分組;

4、使用聚集函數進行計算;

5、使用having子句篩選分組;

6、計算所有的表達式;

7、使用order by對結果集進行排序 。

from 子句--執行順序為從後往前、從右到左

表名(最後面的那個表名為驅動表,執行順序為從後往前, 所以數據量較少的表盡量放後)

oracle 的解析器按照從右到左的順序處理,FROM 子句中的表名,FROM 子句中寫在最後的表(基礎表 driving

table)將被最先處理,即最後的表為驅動表,在FROM 子句中包含多個表的情況下,你必須選擇記錄條數最少的表作為基礎表。如果有3

個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作為基礎表, 交叉表是指被其他表所引用的表

多表連接時,使用表的別名並把別名前綴於每個Column上。可以減少解析的時間並減少那些由Column 歧義引起的語法錯誤.

where子句--執行順序為自下而上、從右到左

ORACLE 採用自下而上從右到左的順序解析Where 子句,根據這個原理,表之間的連接必須寫在其他Where 條件之前, 可以過濾掉最大數量記錄的條件必須寫在Where 子句的末尾。

group by--執行順序從左往右分組

提高GROUP BY 語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉。即在GROUP BY前使用WHERE來過慮,而盡量避免GROUP BY後再HAVING過濾。

having 子句----很耗資源,盡量少用

避免使用HAVING 子句, HAVING 只會在檢索出所有記錄之後才對結果集進行過濾. 這個處理需要排序,總計等操作.

如果能通過Where 子句在GROUP BY前限制記錄的數目,那就能減少這方面的開銷.

(非oracle 中)on、where、having 這三個都可以加條件的子句中,on 是最先執行,where 次之,having 最後,因為on 是先把不符合條件的記錄過濾後才進行統計,它就可以減少中間運算要處理的數據,按理說應該速度是最快的,

where 也應該比having 快點的,因為它過濾數據後才進行sum,在兩個表聯接時才用on 的,所以在一個表的時候,就剩下where 跟having比較了。

在這單表查詢統計的情況下,如果要過濾的條件沒有涉及到要計算欄位,那它們的結果是一樣的,只是where 可以使用rushmore 技術,而having 就不能,在速度上後者要慢。

如果要涉及到計算的欄位,就表示在沒計算之前,這個欄位的值是不確定的,where 的作用時間是在計算之前就完成的,而having 就是在計算後才起作用的,所以在這種情況下,兩者的結果會不同。

在多表聯接查詢時,on 比where 更早起作用。系統首先根據各個表之間的聯接條件,把多個表合成一個臨時表後,再由where 進行過濾,然後再計算,計算完後再由having 進行過濾。

由此可見,要想過濾條件起到正確的作用,首先要明白這個條件應該在什麼時候起作用,然後再決定放在那裡。

select子句--少用*號,盡量取欄位名稱 。

ORACLE 在解析的過程中, 會將依次轉換成所有的列名, 這個工作是通過查詢數據字典完成的, 使用列名意味著將減少消耗時間。

sql 語句用大寫的;因為 oracle 總是先解析 sql 語句,把小寫的字母轉換成大寫的再執行

order by子句--執行順序為從左到右排序,很耗資源