大家都在討論關于數據庫優化方面的東東,剛好參與開發了一個數據倉庫方面的項目,以下的一點東西算是數據庫優化方面的學習+實戰的一些心得體會了,拿出來大家共享。歡迎批評指正阿!

          SQL語句:
          是對數據庫(數據)進行操作的惟一途徑;
          消耗了70%~90%的數據庫資源;獨立于程序設計邏輯,相對于對程序源代碼的優化,對SQL語句的優化在時間成本和風險上的代價都很低;
          可以有不同的寫法;易學,難精通。

          SQL優化:
          固定的SQL書寫習慣,相同的查詢盡量保持相同,存儲過程的效率較高。
          應該編寫與其格式一致的語句,包括字母的大小寫、標點符號、換行的位置等都要一致

          ORACLE優化器:
          在任何可能的時候都會對表達式進行評估,并且把特定的語法結構轉換成等價的結構,這么做的原因是
          要么結果表達式能夠比源表達式具有更快的速度
          要么源表達式只是結果表達式的一個等價語義結構
          不同的SQL結構有時具有同樣的操作(例如:= ANY (subquery) and IN (subquery)),ORACLE會把他們映射到一個單一的語義結構。

          1 常量優化:
          常量的計算是在語句被優化時一次性完成,而不是在每次執行時。下面是檢索月薪大于2000的的表達式:
          sal > 24000/12
          sal > 2000
          sal*12 > 24000
          如果SQL語句包括第一種情況,優化器會簡單地把它轉變成第二種。
          優化器不會簡化跨越比較符的表達式,例如第三條語句,鑒于此,應盡量寫用常量跟字段比較檢索的表達式,而不要將字段置于表達式當中。否則沒有辦法優化,比如如果sal上有索引,第一和第二就可以使用,第三就難以使用。

          2 操作符優化:
          優化器把使用LIKE操作符和一個沒有通配符的表達式組成的檢索表達式轉換為一個“=”操作符表達式。
          例如:優化器會把表達式ename LIKE 'SMITH'轉換為ename = 'SMITH'
          優化器只能轉換涉及到可變長數據類型的表達式,前一個例子中,如果ENAME字段的類型是CHAR(10), 那么優化器將不做任何轉換。
          一般來講LIKE比較難以優化。

          其中:
          ~~ IN 操作符優化:
          優化器把使用IN比較符的檢索表達式替換為等價的使用“=”和“OR”操作符的檢索表達式。
          例如,優化器會把表達式ename IN ('SMITH','KING','JONES')替換為
          ename = 'SMITH' OR ename = 'KING' OR ename = 'JONES‘

          ~~ ANY和SOME 操作符優化:
          優化器將跟隨值列表的ANY和SOME檢索條件用等價的同等操作符和“OR”組成的表達式替換。
          例如,優化器將如下所示的第一條語句用第二條語句替換:
          sal > ANY (:first_sal, :second_sal)
          sal > :first_sal OR sal > :second_sal
          優化器將跟隨子查詢的ANY和SOME檢索條件轉換成由“EXISTS”和一個相應的子查詢組成的檢索表達式。
          例如,優化器將如下所示的第一條語句用第二條語句替換:
          x > ANY (SELECT sal FROM emp WHERE job = 'ANALYST')
          EXISTS (SELECT sal FROM emp WHERE job = 'ANALYST' AND x > sal)

          ~~ ALL操作符優化:
          優化器將跟隨值列表的ALL操作符用等價的“=”和“AND”組成的表達式替換。例如:
          sal > ALL (:first_sal, :second_sal)表達式會被替換為:
          sal > :first_sal AND sal > :second_sal
          對于跟隨子查詢的ALL表達式,優化器用ANY和另外一個合適的比較符組成的表達式替換。例如
          x > ALL (SELECT sal FROM emp WHERE deptno = 10) 替換為:
          NOT (x <= ANY (SELECT sal FROM emp WHERE deptno = 10))
          接下來優化器會把第二個表達式適用ANY表達式的轉換規則轉換為下面的表達式:
          NOT EXISTS (SELECT sal FROM emp WHERE deptno = 10 AND x <= sal)

          ~~ BETWEEN 操作符優化:
          優化器總是用“>=”和“<=”比較符來等價的代替BETWEEN操作符。
          例如:優化器會把表達式sal BETWEEN 2000 AND 3000用sal >= 2000 AND sal <= 3000來代替。

          ~~ NOT 操作符優化:
          優化器總是試圖簡化檢索條件以消除“NOT”邏輯操作符的影響,這將涉及到“NOT”操作符的消除以及代以相應的比較運算符。
          例如,優化器將下面的第一條語句用第二條語句代替:
          NOT deptno = (SELECT deptno FROM emp WHERE ename = 'TAYLOR')
          deptno <> (SELECT deptno FROM emp WHERE ename = 'TAYLOR')
          通常情況下一個含有NOT操作符的語句有很多不同的寫法,優化器的轉換原則是使“NOT”操作符后邊的子句盡可能的簡單,即使可能會使結果表達式包含了更多的“NOT”操作符。
          例如,優化器將如下所示的第一條語句用第二條語句代替:
          NOT (sal < 1000 OR comm IS NULL)
          NOT sal < 1000 AND comm IS NOT NULL sal >= 1000 AND comm IS NOT NULL

          如何編寫高效的SQL:
          當然要考慮sql常量的優化和操作符的優化啦,另外,還需要:

          1 合理的索引設計:
          例:表record有620000行,試看在不同的索引下,下面幾個SQL的運行情況:
          語句A
          SELECT count(*) FROM record
          WHERE date >'19991201' and date < '19991214‘ and amount >2000

          語句B
          SELECT count(*) FROM record
          WHERE date >'19990901' and place IN ('BJ','SH')

          語句C
          SELECT date,sum(amount) FROM record
          group by date
          1 在date上建有一個非聚集索引
          A:(25秒)
          B:(27秒)
          C:(55秒)
          分析:
          date上有大量的重復值,在非聚集索引下,數據在物理上隨機存放在數據頁上,在范圍查找時,必須執行一次表掃描才能找到這一范圍內的全部行。
          2 在date上的一個聚集索引
          A:(14秒)
          B:(14秒)
          C:(28秒)
          分析:
          在聚集索引下,數據在物理上按順序在數據頁上,重復值也排列在一起,因而在范圍查找時,可以先找到這個范圍的起末點,且只在這個范圍內掃描數據頁,避免了大范圍掃描,提高了查詢速度。
          3 在place,date,amount上的組合索引
          A:(26秒)
          C:(27秒)
          B:(< 1秒)
          分析:
          這是一個不很合理的組合索引,因為它的前導列是place,第一和第二條SQL沒有引用place,因此也沒有利用上索引;第三個SQL使用了place,且引用的所有列都包含在組合索引中,形成了索引覆蓋,所以它的速度是非常快的。
          4 在date,place,amount上的組合索引
          A: (< 1秒)
          B:(< 1秒)
          C:(11秒)
          分析:
          這是一個合理的組合索引。它將date作為前導列,使每個SQL都可以利用索引,并且在第一和第三個SQL中形成了索引覆蓋,因而性能達到了最優。

          總結1
          缺省情況下建立的索引是非聚集索引,但有時它并不是最佳的;合理的索引設計要建立在對各種查詢的分析和預測上。一般來說:
          有大量重復值、且經常有范圍查詢(between, >,< ,>=,< =)和order by、group by發生的列,考慮建立聚集索引;
          經 常同時存取多列,且每列都含有重復值可考慮建立組合索引;在條件表達式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不要建立索引。比如在雇員 表的“性別”列上只有“男”與“女”兩個不同值,因此就無必要建立索引。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。
          組合索引要盡量使關鍵查詢形成索引覆蓋,其前導列一定是使用最頻繁的列。

          2 避免使用不兼容的數據類型:
          例如float和INt、char和varchar、bINary和varbINary是不兼容的。數據類型的不兼容可能使優化器無法執行一些本來可以進行的優化操作。例如:
          SELECT name FROM employee WHERE salary > 60000
          在這條語句中,如salary字段是money型的,則優化器很難對其進行優化,因為60000是個整型數。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。

          3 IS NULL 與IS NOT NULL:
          不 能用null作索引,任何包含null值的列都將不會被包含在索引中。即使索引有多列這樣的情況下,只要這些列中有一列含有null,該列就會從索引中排 除。也就是說如果某列存在空值,即使對該列建索引也不會提高性能。任何在WHERE子句中使用is null或is not null的語句優化器是不允 許使用索引的。

          4 IN和EXISTS:
          EXISTS要遠比IN的效率高。里面關系到full table scan和range scan。幾乎將所有的IN操作符子查詢改寫為使用EXISTS的子查詢。
          例子:
          語句1
          SELECT dname, deptno FROM dept
          WHERE deptno NOT IN
          (SELECT deptno FROM emp);
          語句2
          SELECT dname, deptno FROM dept
          WHERE NOT EXISTS
          (SELECT deptno FROM emp
          WHERE dept.deptno = emp.deptno);
          明顯的,2要比1的執行性能好很多
          因為1中對emp進行了full table scan,這是很浪費時間的操作。而且1中沒有用到emp的INdex,
          因為沒有WHERE子句。而2中的語句對emp進行的是range scan。

          5 IN、OR子句常會使用工作表,使索引失效:
          如果不產生大量重復值,可以考慮把子句拆開。拆開的子句中應該包含索引。

          6 避免或簡化排序:
          應當簡化或避免對大型表進行重復的排序。當能夠利用索引自動以適當的次序產生輸出時,優化器就避免了排序的步驟。以下是一些影響因素:
          索引中不包括一個或幾個待排序的列;
          group by或order by子句中列的次序與索引的次序不一樣;
          排序的列來自不同的表。
          為了避免不必要的排序,就要正確地增建索引,合理地合并數據庫表(盡管有時可能影響表的規范化,但相對于效率的提高是值得的)。如果排序不可避免,那么應當試圖簡化它,如縮小排序的列的范圍等。

          7 消除對大型表行數據的順序存取:
          在 嵌套查詢中,對表的順序存取對查詢效率可能產生致命的影響。比如采用順序存取策略,一個嵌套3層的查詢,如果每層都查詢1000行,那么這個查詢就要查詢 10億行數據。避免這種情況的主要方法就是對連接的列進行索引。例如,兩個表:學生表(學號、姓名、年齡??)和選課表(學號、課程號、成績)。如果兩個 表要做連接,就要在“學號”這個連接字段上建立索引。
          還可以使用并集來避免順序存取。盡管在所有的檢查列上都有索引,但某些形式的WHERE子句強迫優化器使用順序存取。下面的查詢將強迫對orders表執行順序操作:
          SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
          雖然在customer_num和order_num上建有索引,但是在上面的語句中優化器還是使用順序存取路徑掃描整個表。因為這個語句要檢索的是分離的行的集合,所以應該改為如下語句:
          SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
          UNION
          SELECT * FROM orders WHERE order_num=1008
          這樣就能利用索引路徑處理查詢。

          8 避免相關子查詢:
          一個列的標簽同時在主查詢和WHERE子句中的查詢中出現,那么很可能當主查詢中的列值改變之后,子查詢必須重新查詢一次。查詢嵌套層次越多,效率越低,因此應當盡量避免子查詢。如果子查詢不可避免,那么要在子查詢中過濾掉盡可能多的行。

          9 避免困難的正規表達式:
          MATCHES和LIKE關鍵字支持通配符匹配,技術上叫正規表達式。但這種匹配特別耗費時間。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
          即使在zipcode字段上建立了索引,在這種情況下也還是采用順序掃描的方式。如果把語句改為SELECT * FROM customer WHERE zipcode >“98000”,在執行查詢時就會利用索引來查詢,顯然會大大提高速度。
          另外,還要避免非開始的子串。例如語句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在WHERE子句中采用了非開始子串,因而這個語句也不會使用索引。

          10 不充份的連接條件:
          例:表card有7896行,在card_no上有一個非聚集索引,表account有191122行,在account_no上有一個非聚集索引,試看在不同的表連接條件下,兩個SQL的執行情況:
          SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no
          (20秒)
          將SQL改為:
          SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no and a.account_no=b.account_no
          (< 1秒)
          分析:
          在第一個連接條件下,最佳查詢方案是將account作外層表,card作內層表,利用card上的索引,其I/O次數可由以下公式估算為:
          外層表account上的22541頁+(外層表account的191122行*內層表card上對應外層表第一行所要查找的3頁)=595907次I/O
          在第二個連接條件下,最佳查詢方案是將card作外層表,account作內層表,利用account上的索引,其I/O次數可由以下公式估算為:
          外層表card上的1944頁+(外層表card的7896行*內層表account上對應外層表每一行所要查找的4頁)= 33528次I/O
          可見,只有充份的連接條件,真正的最佳方案才會被執行。
          多表操作在被實際執行前,查詢優化器會根據連接條件,列出幾組可能的連接方案并從中找出系統開銷最小的最佳方案。連接條件要充份考慮帶有索引的表、行數多的表;內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次查找的次數確定,乘積最小為最佳方案。
          不可優化的WHERE子句
          例1
          下列SQL條件語句中的列都建有恰當的索引,但執行速度卻非常慢:
          SELECT * FROM record WHERE substrINg(card_no,1,4)='5378'
          (13秒)
          SELECT * FROM record WHERE amount/30< 1000
          (11秒)
          SELECT * FROM record WHERE convert(char(10),date,112)='19991201'
          (10秒)
          分析:
          WHERE子句中對列的任何操作結果都是在SQL運行時逐列計算得到的,因此它不得不進行表搜索,而沒有使用該列上面的索引;如果這些結果在查詢編譯時就能得到,那么就可以被SQL優化器優化,使用索引,避免表搜索,因此將SQL重寫成下面這樣:
          SELECT * FROM record WHERE card_no like '5378%'
          (< 1秒)
          SELECT * FROM record WHERE amount< 1000*30
          (< 1秒)
          SELECT * FROM record WHERE date= '1999/12/01'
          (< 1秒)

          11 存儲過程中,采用臨時表優化查詢:

          1.從parven表中按vendor_num的次序讀數據:
          SELECT part_num,vendor_num,price FROM parven ORDER BY vendor_num
          INTO temp pv_by_vn
          這個語句順序讀parven(50頁),寫一個臨時表(50頁),并排序。假定排序的開銷為200頁,總共是300頁。
          2.把臨時表和vendor表連接,把結果輸出到一個臨時表,并按part_num排序:
          SELECT pv_by_vn,* vendor.vendor_num FROM pv_by_vn,vendor
          WHERE pv_by_vn.vendor_num=vendor.vendor_num
          ORDER BY pv_by_vn.part_num
          INTO TMP pvvn_by_pn
          DROP TABLE pv_by_vn
          這 個查詢讀取pv_by_vn(50頁),它通過索引存取vendor表1.5萬次,但由于按vendor_num次序排列,實際上只是通過索引順序地讀 vendor表(40+2=42頁),輸出的表每頁約95行,共160頁。寫并存取這些頁引發5*160=800次的讀寫,索引共讀寫892頁。
          3.把輸出和part連接得到最后的結果:
          SELECT pvvn_by_pn.*,part.part_desc FROM pvvn_by_pn,part
          WHERE pvvn_by_pn.part_num=part.part_num
          DROP TABLE pvvn_by_pn
          這樣,查詢順序地讀pvvn_by_pn(160頁),通過索引讀part表1.5萬次,由于建有索引,所以實際上進行1772次磁盤讀寫,優化比例為30∶1。

          好了,搞定。
          其實sql的優化,各種數據庫之間都是互通的


          影響SQL server性能的關鍵三個方面(轉貼)

          關鍵詞:Sql Server

          轉貼自:http://202.200.129.193/
          1 邏輯數據庫和表的設計
          數據庫的邏輯設計、包括表與表之間的關系是優化關系型數據庫性能的核心。一個好的邏輯數據庫設計可以為

          優化數據庫和應用程序打下良好的基礎。

          標準化的數據庫邏輯設計包括用多的、有相互關系的窄表來代替很多列的長數據表。下面是一些使用標準化

          表的一些好處。

          A:由于表窄,因此可以使排序和建立索引更為迅速

          B:由于多表,所以多鏃的索引成為可能

          C:更窄更緊湊的索引

          D:每個表中可以有少一些的索引,因此可以提高insert update delete等的速度,因為這些操作在索引

          多的情況下會對系統性能產生很大的影響

          E:更少的空值和更少的多余值,增加了數據庫的緊湊性

          由于標準化,所以會增加了在獲取數據時引用表的數目和其間的連接關系的復雜性。太多的表和復雜的連接關系會降低服務器的性能,因此在這兩者之間需要綜合考慮。
          定義具有相關關系的主鍵和外來鍵時應該注意的事項主要是:用于連接多表的主鍵和參考的鍵要有相同的數據類型。

          2 索引的設計
          A:盡量避免表掃描
          檢查你的查詢語句的where子句,因為這是優化器重要關注的地方。包含在where里面的每一列(column)都是可能的侯選索引,為能達到最優的性能,考慮在下面給出的例子:對于在where子句中給出了column1這個列。
          下面的兩個條件可以提高索引的優化查詢性能!
          第一:在表中的column1列上有一個單索引
          第二:在表中有多索引,但是column1是第一個索引的列
          避免定義多索引而column1是第二個或后面的索引,這樣的索引不能優化服務器性能
          例如:下面的例子用了pubs數據庫。
          SELECT au_id, au_lname, au_fname FROM authors
          WHERE au_lname = 'White'
          按下面幾個列上建立的索引將會是對優化器有用的索引
          ?au_lname
          ?au_lname, au_fname
          而在下面幾個列上建立的索引將不會對優化器起到好的作用
          ?au_address
          ?au_fname, au_lname
          考慮使用窄的索引在一個或兩個列上,窄索引比多索引和復合索引更能有效。用窄的索引,在每一頁上
          將會有更多的行和更少的索引級別(相對與多索引和復合索引而言),這將推進系統性能。
          對于多列索引,SQL Server維持一個在所有列的索引上的密度統計(用于聯合)和在第一個索引上的
          histogram(柱狀圖)統計。根據統計結果,如果在復合索引上的第一個索引很少被選擇使用,那么優化器對很多查詢請求將不會使用索引。
          有用的索引會提高select語句的性能,包括insert,uodate,delete。
          但是,由于改變一個表的內容,將會影響索引。每一個insert,update,delete語句將會使性能下降一些。實驗表明,不要在一個單表上用大量的索引,不要在共享的列上(指在多表中用了參考約束)使用重疊的索引。
          在某一列上檢查唯一的數據的個數,比較它與表中數據的行數做一個比較。這就是數據的選擇性,這比較結果將會幫助你決定是否將某一列作為侯選的索引列,如果需要,建哪一種索引。你可以用下面的查詢語句返回某一列的不同值的數目。
          select count(distinct cloumn_name) from table_name
          假設column_name是一個10000行的表,則看column_name返回值來決定是否應該使用,及應該使用什么索引。
          Unique values Index

          5000 Nonclustered index
          20 Clustered index
          3 No index

          鏃索引和非鏃索引的選擇

          <1:>鏃索引是行的物理順序和索引的順序是一致的。頁級,低層等索引的各個級別上都包含實際的數據頁。一個表只能是有一個鏃索引。由于update,delete語句要求相對多一些的讀操作,因此鏃索引常常能加速這樣的操作。在至少有一個索引的表中,你應該有一個鏃索引。
          在下面的幾個情況下,你可以考慮用鏃索引:
          例如: 某列包括的不同值的個數是有限的(但是不是極少的)
          顧客表的州名列有50個左右的不同州名的縮寫值,可以使用鏃索引。
          例如: 對返回一定范圍內值的列可以使用鏃索引,比如用between,>,>=,<,<=等等來對列進行操作的列上。
          select * from sales where ord_date between '5/1/93' and '6/1/93'
          例如: 對查詢時返回大量結果的列可以使用鏃索引。
          SELECT * FROM phonebook WHERE last_name = 'Smith'

          當有大量的行正在被插入表中時,要避免在本表一個自然增長(例如,identity列)的列上建立鏃索引。如果你建立了鏃的索引,那么insert的性能就會大大降低。因為每一個插入的行必須到表的最后,表的最后一個數據頁。
          當一個數據正在被插入(這時這個數據頁是被鎖定的),所有的其他插入行必須等待直到當前的插入已經結束。
          一個索引的葉級頁中包括實際的數據頁,并且在硬盤上的數據頁的次序是跟鏃索引的邏輯次序一樣的。

          <2:>一個非鏃的索引就是行的物理次序與索引的次序是不同的。一個非鏃索引的葉級包含了指向行數據頁的指針。
          在一個表中可以有多個非鏃索引,你可以在以下幾個情況下考慮使用非鏃索引。
          在有很多不同值的列上可以考慮使用非鏃索引
          例如:一個part_id列在一個part表中
          select * from employee where emp_id = 'pcm9809f'
          查詢語句中用order by 子句的列上可以考慮使用鏃索引

          3 查詢語句的設計

          SQL Server優化器通過分析查詢語句,自動對查詢進行優化并決定最有效的執行方案。優化器分析查詢語句來決定那個子句可以被優化,并針對可以被優化查詢的子句來選擇有用的索引。最后優化器比較所有可能的執行方案并選擇最有效的一個方案出來。
          在執行一個查詢時,用一個where子句來限制必須處理的行數,除非完全需要,否則應該避免在一個表中無限制地讀并處理所有的行。
          例如下面的例子,
          select qty from sales where stor_id=7131
          是很有效的比下面這個無限制的查詢
          select qty from sales
          避免給客戶的最后數據選擇返回大量的結果集。允許SQL Server運行滿足它目的的函數限制結果集的大小是更有效的。
          這能減少網絡I/O并能提高多用戶的相關并發時的應用程序性能。因為優化器關注的焦點就是where子句的查詢,以利用有用的索引。在表中的每一個索引都可能成為包括在where子句中的侯選索引。為了最好的性能可以遵照下面的用于一個給定列column1的索引。
          第一:在表中的column1列上有一個單索引
          第二:在表中有多索引,但是column1是第一個索引的列不要在where子句中使用沒有column1列索引的查詢語句,并避免在where子句用一個多索引的非第一個索引的索引。
          這時多索引是沒有用的。
          For example, given a multicolumn index on the au_lname, au_fname columns of the authors table in
          the pubs database,
          下面這個query語句利用了au_lname上的索引
          SELECT au_id, au_lname, au_fname FROM authors
          WHERE au_lname = 'White'
          AND au_fname = 'Johnson'
          SELECT au_id, au_lname, au_fname FROM authors
          WHERE au_lname = 'White'
          下面這個查詢沒有利用索引,因為他使用了多索引的非第一個索引的索引
          SELECT au_id, au_lname, au_fname FROM authors
          WHERE au_fname = 'Johnson'



          posted on 2006-10-23 15:23 jackstudio 閱讀(519) 評論(0)  編輯  收藏 所屬分類: commonDatabase
          主站蜘蛛池模板: 耒阳市| 宝鸡市| 新余市| 黄梅县| 曲水县| 长治县| 雷波县| 辛集市| 平谷区| 子洲县| 邹平县| 巨野县| 昌图县| 汉阴县| 重庆市| 双辽市| 米泉市| 湖州市| 民和| 谢通门县| 洛扎县| 讷河市| 蓬溪县| 正宁县| 通州市| 东安县| 巴中市| 通渭县| 甘孜| 二连浩特市| 丰顺县| 密云县| 军事| SHOW| 渭源县| 大渡口区| 社旗县| 松原市| 灵武市| 福泉市| 余姚市|