SQL的執(zhí)行順序

sql語句的執(zhí)行步驟:

1)語法分析,分析語句的語法是否符合規(guī)范,衡量語句中各表達(dá)式的意義。

2)語義分析,檢查語句中涉及的所有數(shù)據(jù)庫對象是否存在,且用戶有相應(yīng)的權(quán)限

3)視圖轉(zhuǎn)換,將涉及視圖的查詢語句轉(zhuǎn)換為相應(yīng)的對基表查詢語句。

4)表達(dá)式轉(zhuǎn)換, 將復(fù)雜的 SQL 表達(dá)式轉(zhuǎn)換為較簡單的等效連接表達(dá)式。

5)選擇優(yōu)化器,不同的優(yōu)化器一般產(chǎn)生不同的“執(zhí)行計劃”

6)選擇連接方式, ORACLE 有三種連接方式,對多表連接 ORACLE 可選擇適當(dāng)?shù)倪B接方式。

7)選擇連接順序, 對多表連接 ORACLE 選擇哪一對表先連接,選擇這兩表中哪個表做為源數(shù)據(jù)表。

8)選擇數(shù)據(jù)的搜索路徑,根據(jù)以上條件選擇合適的數(shù)據(jù)搜索路徑,如是選用全表搜索還是利用索引或是其他的方式。

9)運(yùn)行“執(zhí)行計劃”。

Oracle共享原理:將執(zhí)行過的SQL語句存放在內(nèi)存的共享池(shared buffer pool)中,可以被所有的數(shù)據(jù)庫用戶共享當(dāng)你執(zhí)行一個SQL語句(有時被稱為一個游標(biāo))時,如果它和之前的執(zhí)行過的語句完全相同, Oracle就能很快獲得已經(jīng)被解析的語句以及最好的 執(zhí)行路徑. 這個功能大大地提高了SQL的執(zhí)行性能并節(jié)省了內(nèi)存的使用。

SQL查詢處理的步驟序號:

(8) SELECT (9) DISTINCT (11) ?

(1) FROM ?

(3) JOIN ?

(2) ON ?

(4) WHERE ?

(5) GROUP BY ?

(6) WITH {CUBE | ROLLUP}?

(7) HAVING ?

(10) ORDER BY ?

以上每個步驟都會產(chǎn)生一個虛擬表,該虛擬表被用作下一個步驟的輸入。這些虛擬表對調(diào)用者(客戶端應(yīng)用程序或者外部查詢)不可用。只有最后一步生成的表才會會給調(diào)用者。如果沒有在查詢中指定某一個子句,將跳過相應(yīng)的步驟。

邏輯查詢處理階段簡介:

1、 FROM:對FROM子句中的前兩個表執(zhí)行笛卡爾積(交叉聯(lián)接),生成虛擬表VT1。表名執(zhí)行順序是從后往前,所以數(shù)據(jù)較少的表盡量放后。

2、 ON:對VT1應(yīng)用ON篩選器,只有那些使為真才被插入到TV2。

3、 OUTER (JOIN):如果指定了OUTER JOIN(相對于CROSS JOIN或INNER JOIN),保留表中未找到匹配的行將作為外部行添加到VT2,生成TV3。如果FROM子句包含兩個以上的表,則對上一個聯(lián)接生成的結(jié)果表和下一個表重復(fù)執(zhí)行步驟1到步驟3,直到處理完所有的表位置。

4、 WHERE:對TV3應(yīng)用WHERE篩選器,只有使為true的行才插入TV4。執(zhí)行順序為從前往后或者說從左到右。

5、 GROUP BY:按GROUP BY子句中的列列表對TV4中的行進(jìn)行分組,生成TV5。執(zhí)行順序從左往右分組。

6、 CUTE|ROLLUP:把超組插入VT5,生成VT6。

7、 HAVING:對VT6應(yīng)用HAVING篩選器,只有使為true的組插入到VT7。Having語句很耗資源,盡量少用

8、 SELECT:處理SELECT列表,產(chǎn)生VT8。

9、 DISTINCT:將重復(fù)的行從VT8中刪除,產(chǎn)品VT9。

10、ORDER BY:將VT9中的行按ORDER BY子句中的列列表順序,生成一個游標(biāo)(VC10)。執(zhí)行順序從左到右,是一個很耗資源的語句。

11、TOP:從VC10的開始處選擇指定數(shù)量或比例的行,生成表TV11,并返回給調(diào)用者



?提高SQL的執(zhí)行效率

第一、只返回需要的數(shù)據(jù)

返回數(shù)據(jù)到客戶端至少需要數(shù)據(jù)庫提取數(shù)據(jù)、網(wǎng)絡(luò)傳輸數(shù)據(jù)、客戶端接收數(shù)據(jù)以及客戶端處理數(shù)據(jù)等環(huán)節(jié),如果返回不需要的數(shù)據(jù),就會增加服務(wù)器、網(wǎng)絡(luò)和客戶端的無效勞動,其害處是顯而易見的,避免這類事件需要注意:

A、橫向來看,

(1)不要寫SELECT *的語句,而是選擇你需要的字段。

(2)當(dāng)在SQL語句中連接多個表時, 請使用表的別名并把別名前綴于每個Column上.這樣一來,就可以減少解析的時間并減少那些由Column歧義引起的語法錯誤。

B、縱向來看,

(1)合理寫WHERE子句,不要寫沒有WHERE的SQL語句。

(2) SELECT TOP N * --沒有WHERE條件的用此替代

? 第二、盡量少做重復(fù)的工作

A、控制同一語句的多次執(zhí)行,特別是一些基礎(chǔ)數(shù)據(jù)的多次執(zhí)行是很多程序員很少注意的。

B、減少多次的數(shù)據(jù)轉(zhuǎn)換,也許需要數(shù)據(jù)轉(zhuǎn)換是設(shè)計的問題,但是減少次數(shù)是程序員可以做到的。

C、杜絕不必要的子查詢和連接表,子查詢在執(zhí)行計劃一般解釋成外連接,多余的連接表帶來額外的開銷。

D、合并對同一表同一條件的多次UPDATE。

E、UPDATE操作不要拆成DELETE操作+INSERT操作的形式,雖然功能相同,但是性能差別是很大的。

第三、注意臨時表和表變量的用法

在復(fù)雜系統(tǒng)中,臨時表和表變量很難避免,關(guān)于臨時表和表變量的用法,需要注意:

A、如果語句很復(fù)雜,連接太多,可以考慮用臨時表和表變量分步完成。

B、如果需要多次用到一個大表的同一部分?jǐn)?shù)據(jù),考慮用臨時表和表變量暫存這部分?jǐn)?shù)據(jù)。

C、如果需要綜合多個表的數(shù)據(jù),形成一個結(jié)果,可以考慮用臨時表和表變量分步匯總這多個表的數(shù)據(jù)。

D、其他情況下,應(yīng)該控制臨時表和表變量的使用。

E、關(guān)于臨時表和表變量的選擇,很多說法是表變量在內(nèi)存,速度快,應(yīng)該首選表變量,但是在實(shí)際使用中發(fā)現(xiàn),(1)主要考慮需要放在臨時表的數(shù)據(jù)量,在數(shù)據(jù)量較多的情況下,臨時表的速度反而更快。(2)執(zhí)行時間段與預(yù)計執(zhí)行時間(多長)

F、關(guān)于臨時表產(chǎn)生使用SELECT INTO和CREATE TABLE + INSERT INTO的選擇,一般情況下,SELECT INTO會比CREATE TABLE + INSERT INTO的方法快很多,但是SELECT INTO會鎖定TEMPDB的系統(tǒng)表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用戶并發(fā)環(huán)境下,容易阻塞其他進(jìn)程,所以我的建議是,在并發(fā)系統(tǒng)中,盡量使用CREATE TABLE + INSERT INTO,而大數(shù)據(jù)量的單個語句使用中,使用SELECT INTO。

第四、注意子查詢的用法

子查詢是一個 SELECT 查詢,它嵌套在 SELECT、INSERT、UPDATE、DELETE 語句或其它子查詢中。任何允許使用表達(dá)式的地方都可以使用子查詢,子查詢可以使我們的編程靈活多樣,可以用來實(shí)現(xiàn)一些特殊的功能。但是在性能上,往往一個不合適的子查詢用法會形成一個性能瓶頸。如果子查詢的條件中使用了其外層的表的字段,這種子查詢就叫作相關(guān)子查詢。

相關(guān)子查詢可以用IN、NOT IN、EXISTS、NOT EXISTS引入。 關(guān)于相關(guān)子查詢,應(yīng)該注意:

(1)

A、NOT IN、NOT EXISTS的相關(guān)子查詢可以改用LEFT JOIN代替寫法。

比如:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID NOT IN (SELECT PUB_ID FROM TITLES WHERE TYPE = 'BUSINESS')

可以改寫成:

SELECT A.PUB_NAME

FROM PUBLISHERS A LEFT JOIN TITLES B ON B.TYPE = 'BUSINESS' AND A.PUB_ID=B. PUB_ID

WHERE B.PUB_ID IS NULL

(2)

SELECT TITLE

FROM TITLES

WHERE NOT EXISTS (SELECT TITLE_ID FROM SALES WHERE TITLE_ID = TITLES.TITLE_ID)

可以改寫成:

SELECT TITLE

FROM?TITLES LEFT JOIN SALES ON SALES.TITLE_ID = TITLES.TITLE_ID

WHERE SALES.TITLE_ID IS NULL

B、 如果保證子查詢沒有重復(fù) ,IN、EXISTS的相關(guān)子查詢可以用INNER JOIN 代替。

比如:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID IN (SELECT PUB_ID FROM TITLES

WHERE TYPE = 'BUSINESS')

可以改寫成:

SELECT DISTINCT A.PUB_NAME

FROM PUBLISHERS A INNER JOIN TITLES B ON B.TYPE = 'BUSINESS' AND A.PUB_ID=B. PUB_ID

C、 IN的相關(guān)子查詢用EXISTS代替,比如

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID IN (SELECT PUB_ID FROM TITLES WHERE TYPE = 'BUSINESS')

可以用下面語句代替:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE EXISTS (SELECT 1 FROM TITLES WHERE TYPE = 'BUSINESS' AND PUB_ID= PUBLISHERS.PUB_ID) D、不要用COUNT(*)的子查詢判斷是否存在記錄,最好用LEFT JOIN或者EXISTS,比如有人寫這樣的語句:

SELECT JOB_DESC

FROM JOBS

WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)=0

應(yīng)該改成:

SELECT JOBS.JOB_DESC

FROM JOBS LEFT JOIN EMPLOYEE ON EMPLOYEE.JOB_ID=JOBS.JOB_ID

WHERE EMPLOYEE.EMP_ID IS NULL

SELECT JOB_DESC FROM JOBS WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)<>0

應(yīng)該改成:

SELECT JOB_DESC FROM JOBS

WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)?

第五、盡量使用索引,并注意對含索引列的運(yùn)算

建立索引后,并不是每個查詢都會使用索引,在使用索引的情況下,索引的使用效率也會有很大的差別。只要我們在查詢語句中沒有強(qiáng)制指定索引,索引的選擇和使用方法是SQLSERVER的優(yōu)化器自動作的選擇,而它選擇的根據(jù)是查詢語句的條件以及相關(guān)表的統(tǒng)計信息,這就要求我們在寫SQL語句的時候盡量使得優(yōu)化器可以使用索引。為了使得優(yōu)化器能高效使用索引,寫語句的時候應(yīng)該注意:

A、不要對索引字段進(jìn)行運(yùn)算,而要想辦法做變換,比如

SELECT ID FROM T WHERE NUM/2=100

應(yīng)改為:

SELECT ID FROM T WHERE NUM=100*2

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

SELECT ID FROM T WHERE NUM/2=NUM1

如果NUM有索引應(yīng)改為:

SELECT ID FROM T WHERE NUM=NUM1*2

如果NUM1有索引則不應(yīng)該改。

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

發(fā)現(xiàn)過這樣的語句:

SELECT 年,月,金額 FROM 結(jié)余表 WHERE 100*年+月=2010*100+10

應(yīng)該改為:

SELECT 年,月,金額 FROM 結(jié)余表 WHERE 年=2010 AND月=10

B、 不要對索引字段進(jìn)行格式轉(zhuǎn)換

日期字段的例子:

WHERE CONVERT(VARCHAR(10), 日期字段,120)='2010-07-15'

應(yīng)該改為

WHERE日期字段〉='2010-07-15' AND 日期字段<'2010-07-16' IS NULL

轉(zhuǎn)換的例子:

WHERE ISNULL(字段,'')<>''應(yīng)改為:WHERE字段<>'

WHERE ISNULL(字段,'')=''不應(yīng)修改

WHERE ISNULL(字段,'F') ='T'應(yīng)改為: WHERE字段='T'

WHERE ISNULL(字段,'F')<>'T'不應(yīng)修改

C、 不要對索引字段使用函數(shù)

WHERE LEFT(NAME, 3)='ABC' 或者WHERE SUBSTRING(NAME,1, 3)='ABC'

應(yīng)改為: WHERE NAME LIKE 'ABC%'

日期查詢的例子:

WHERE DATEDIFF(DAY, 日期,'2010-06-30')=0

應(yīng)改為:WHERE 日期>='2010-06-30' AND 日期 <'2010-07-01'

WHERE DATEDIFF(DAY, 日期,'2010-06-30')>0

應(yīng)改為:WHERE 日期 <'2010-06-30'

WHERE DATEDIFF(DAY, 日期,'2010-06-30')>=0

應(yīng)改為:WHERE 日期 <'2010-07-01'

WHERE DATEDIFF(DAY, 日期,'2010-06-30')<0

應(yīng)改為:WHERE 日期>='2010-07-01'

WHERE DATEDIFF(DAY, 日期,'2010-06-30')<=0

應(yīng)改為:WHERE 日期>='2010-06-30'

D、不要對索引字段進(jìn)行多字段連接

比如:

WHERE FAME+ '. '+LNAME='HAIWEI.YANG'

應(yīng)改為:

WHERE FNAME='HAIWEI' AND LNAME='YANG'

第六、注意多表連接的連接條件的選擇與表示

多表連接的連接條件對索引的選擇有著重要的意義,所以我們在寫連接條件條件的時候需要特別注意。

A、多表連接的時候,連接條件必須寫全,寧可重復(fù),不要缺漏。

B、連接條件盡量使用聚集索引

C、注意ON、WHERE和HAVING部分條件的區(qū)別: ON是最先執(zhí)行, WHERE次之,HAVING最后,因為ON是先把不符合條件的記錄過濾后才進(jìn)行統(tǒng)計,它就可以減少中間運(yùn)算要處理的數(shù)據(jù),按理說應(yīng)該速度是最快的,WHERE也應(yīng)該比 HAVING快點(diǎn)的,因為它過濾數(shù)據(jù)后才進(jìn)行SUM,在兩個表聯(lián)接時才用ON的,所以在一個表的時候,就剩下WHERE跟HAVING比較了

1、考慮聯(lián)接優(yōu)先順序:

2、INNER JOIN

3、LEFT JOIN (注:RIGHT JOIN 用 LEFT JOIN 替代)

4、CROSS JOIN

其它注意和了解的地方有:

A、在IN后面值的列表中,將出現(xiàn)最頻繁的值放在最前面,出現(xiàn)得最少的放在最后面,減少判斷的次數(shù)

B、注意UNION和UNION ALL的區(qū)別。--允許重復(fù)數(shù)據(jù)用UNION ALL好

C、注意使用DISTINCT,在沒有必要時不要用

D、TRUNCATE TABLE 與 DELETE 區(qū)別

E、減少訪問數(shù)據(jù)庫的次數(shù)

還有就是我們寫存儲過程,如果比較長的話,最后用標(biāo)記符標(biāo)開,因為這樣可讀性很好,即使語句寫的不怎么樣但是語句工整。如:

?????????? --startof 查詢在職人數(shù)

?????????????? sql語句

????????? ?--end of

???????正式機(jī)器上我們一般不能隨便調(diào)試程序,但是很多時候程序在我們本機(jī)上沒問題,但是進(jìn)正式系統(tǒng)就有問題,但是我們又不能隨便在正式機(jī)器上操作,那么怎么辦呢?我們可以用回滾來調(diào)試我們的存儲過程或者是sql語句,從而排錯。

??????? BEGIN TRAN

??????????? UPDATE a SET 字段='? '

??????? ROLLBACK

作業(yè)存儲過程可以加上下面這段,這樣檢查錯誤可以放在存儲過程,如果執(zhí)行錯誤回滾操作,但是如果程序里面已經(jīng)有了事務(wù)回滾,那么存儲過程就不要寫事務(wù)了,這樣會導(dǎo)致事務(wù)回滾嵌套降低執(zhí)行效率,但是我們很多時候可以把檢查放在存儲過程里,這樣有利于我們解讀這個存儲過程,和排錯。

BEGIN TRANSACTION

??????? --事務(wù)回滾開始

???????????? ?--檢查報錯

????????????? IF ( @@ERROR > 0 )

????????????????? BEGIN

????????????????? ?? --回滾操作

?????????????????????ROLLBACK TRANSACTION

??????????????????? ?RAISERROR('刪除工作報告錯誤', 16, 3)

??????????????? ??RETURN

????????????????? END

???????? --結(jié)束事務(wù)

???????? COMMIT TRANSACTION?

第七、有效使用Decode函數(shù)

?????????????使用Decode函數(shù)可以有效避免重復(fù)掃描相同數(shù)據(jù)或重復(fù)連接相同表。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 查詢語句中select from where group by having order by limit的執(zhí)行順...
    許小小晴閱讀 3,297評論 1 3
  • 50個常用的sql語句Student(S#,Sname,Sage,Ssex) 學(xué)生表Course(C#,Cname...
    哈哈海閱讀 1,334評論 0 7
  • 這一月來說媽媽整體是比較輕松的,寶貝回姑姑家及你的老家呆了十天左右,之后爸爸媽媽又把你接回了自己家,寶貝...
    飛揚(yáng)的青春33閱讀 731評論 0 2
  • 很多女生在結(jié)婚前,面對讓自己并不滿意的另一半的時候,都會告訴自己:“結(jié)婚以后就好了。” 結(jié)婚以后,他就會成熟了吧。...
    洛城閱讀 2,136評論 31 42
  • 七天訓(xùn)第三天 學(xué)習(xí)感悟 錢武暉 今天七天訓(xùn)第三天,學(xué)習(xí)了破擦音六個、鼻音三個、舌邊音一個、半元音兩個,共計12個輔...
    雨過天晴love閱讀 552評論 0 0

友情鏈接更多精彩內(nèi)容