數據庫建表原則,SQL數據庫建表前期優化,SQL數據庫操做優化,數據庫命名規範

關鍵字: 數據庫建表原則html

·1. 原始單據與實體之間的關係程序員

能夠是一對1、一對多、多對多的關係。在通常狀況下,它們是一對一的關係:即一張原始單據對應且只對應一個實體。在特殊狀況下,它們多是一對多或多對一的關係,即一張原始單證對應多個實體,或多張原始單證對應一個實體。這裏的實體能夠理解爲基本表。明確這種對應關係後,對咱們設計錄入界面大有好處。算法

〖例〗:一份員工履歷資料,在人力資源信息系統中,就對應三個基本表:員工基本狀況表、社會關係表、工做簡歷表。這就是「一張原始單證對應多個實體」的典型例子。數據庫

 

·2. 主鍵與外鍵編程

通常而言,一個實體不能既無主鍵又無外鍵。在E—R 圖中, 處於葉子部位的實體, 能夠定義主鍵,也能夠不定義主鍵(由於它無子孫), 但必需要有外鍵(由於它有父親)。緩存

主鍵與外鍵的設計,在全局數據庫的設計中,佔有重要地位。當全局數據庫的設計完成之後,有個美國數據庫設計專家說:「鍵,處處都是鍵,除了鍵以外,什麼也沒有」,這就是他的數據庫設計經驗之談,也反映了他對信息系統核心(數據模型)的高度抽象思想。由於:主鍵是實體的高度抽象,主鍵與外鍵的配對,表示實體之間的鏈接。安全

 

·3. 基本表的性質性能優化

基本表與中間表、臨時表不一樣,由於它具備以下四個特性:服務器

(1) 原子性。基本表中的字段是不可再分解的。網絡

(2) 原始性。基本表中的記錄是原始數據(基礎數據)的記錄。

(3) 演繹性。由基本表與代碼表中的數據,能夠派生出全部的輸出數據。

(4) 穩定性。基本表的結構是相對穩定的,表中的記錄是要長期保存的。

理解基本表的性質後,在設計數據庫時,就能將基本表與中間表、臨時表區分開來。

 

·4. 範式標準

基本表及其字段之間的關係, 應儘可能知足第三範式。可是,知足第三範式的數據庫設計,每每不是最好的設計。爲了提升數據庫的運行效率,經常須要下降範式標準:適當增長冗餘,達到以空間換時間的目的。

〖例〗:有一張存放商品的基本表,如表1所示。「金額」這個字段的存在,代表該表的設計不知足第三範式,由於「金額」能夠由「單價」乘以「數量」獲得,說明「金額」是冗餘字段。可是,增長「金額」這個冗餘字段,能夠提升查詢統計的速度,這就是以空間換時間的做法。

在Rose 2002中,規定列有兩種類型:數據列和計算列。「金額」這樣的列被稱爲「計算列」,而「單價」和「數量」這樣的列被稱爲「數據列」。

表1 商品表的表結構

商品名稱  商品型號  單價  數量   金額

電視機    29吋      2,500  40  100,000

 

·5. 通俗地理解三個範式

通俗地理解三個範式,對於數據庫設計大有好處。在數據庫設計中,爲了更好地應用三個範式,就必須通俗地理解三個範式(通俗地理解是夠用的理解,並非最科學最準確的理解):

第一範式:1NF是對屬性的原子性約束,要求屬性具備原子性,不可再分解;

第二範式:2NF是對記錄的唯一性約束,要求記錄有唯一標識,即實體的唯一性;

第三範式:3NF是對字段冗餘性的約束,即任何字段不能由其餘字段派生出來,它要求字段沒有冗餘.

沒有冗餘的數據庫設計能夠作到。可是,沒有冗餘的數據庫未必是最好的數據庫,有時爲了提升運行效率,就必須下降範式標準,適當保留冗餘數據。具體作法是:在概念數據模型設計時遵照第三範式,下降範式標準的工做放到物理數據模型設計時考慮。下降範式就是增長字段,容許冗餘。

 

·6. 要善於識別與正確處理多對多的關係

若兩個實體之間存在多對多的關係,則應消除這種關係。消除的辦法是,在二者之間增長第三個實體。這樣,原來一個多對多的關係,如今變爲兩個一對多的關係。要將原來兩個實體的屬性合理地分配到三個實體中去。這裏的第三個實體,實質上是一個較複雜的關係,它對應一張基本表。通常來說,數據庫設計工具不能識別多對多的關係,但能處理多對多的關係。

〖例〗:在「圖書館信息系統」中,「圖書」是一個實體,「讀者」也是一個實體。這兩個實體之間的關係,是一個典型的多對多關係:一本圖書在不一樣時間能夠被多個讀者借閱,一個讀者又能夠借多本圖書。爲此,要在兩者之間增長第三個實體,該實體取名爲「借還書」,它的屬性爲:借還時間、借還標誌(0表示借書,1表示還書),另外,它還應該有兩個外鍵(「圖書」的主鍵,「讀者」的主鍵),使它能與「圖書」和「讀者」鏈接。

 

·7. 主鍵PK的取值方法

PK是供程序員使用的表間鏈接工具,能夠是一無物理意義的數字串, 由程序自動加1來實現。也能夠是有物理意義的字段名或字段名的組合。不過前者比後者好。當PK是字段名的組合時,建議字段的個數不要太多,多了不但索引佔用空間大,並且速度也慢。

 

·8. 正確認識數據冗餘

主鍵與外鍵在多表中的重複出現,不屬於數據冗餘,這個概念必須清楚,事實上有許多人還不清楚。非鍵字段的重複出現, 纔是數據冗餘!並且是一種低級冗餘,即重複性的冗餘。高級冗餘不是字段的重複出現,而是字段的派生出現。

〖例〗:商品中的「單價、數量、金額」三個字段,「金額」就是由「單價」乘以「數量」派生出來的,它就是冗餘,並且是一種高級冗餘。冗餘的目的是爲了提升處理速度。只有低級冗餘纔會增長數據的不一致性,由於同一數據,可能從不一樣時間、地點、角色上屢次錄入。所以,咱們提倡高級冗餘(派生性冗餘),反對低級冗餘(重複性冗餘)。

消費流水錶的 原餘額,現餘額,消費金額其中原餘額冗餘 ,可是也要保留。

 

·9. E--R圖沒有標準答案

信息系統的E--R圖沒有標準答案,由於它的設計與畫法不是唯一的,只要它覆蓋了系統需求的業務範圍和功能內容,就是可行的。反之要修改E--R圖。儘管它沒有唯一的標準答案,並不意味着能夠隨意設計。好的E—R圖的標準是:結構清晰、關聯簡潔、實體個數適中、屬性分配合理、沒有低級冗餘。

 

·10. 視圖技術在數據庫設計中頗有用

與基本表、代碼表、中間表不一樣,視圖是一種虛表,它依賴數據源的實表而存在。視圖是供程序員使用數據庫的一個窗口,是基表數據綜合的一種形式, 是數據處理的一種方法,是用戶數據保密的一種手段。爲了進行復雜處理、提升運算速度和節省存儲空間, 視圖的定義深度通常不得超過三層。 若三層視圖仍不夠用, 則應在視圖上定義臨時表, 在臨時表上再定義視圖。這樣反覆交迭定義, 視圖的深度就不受限制了。

對於某些與國家政治、經濟、技術、軍事和安全利益有關的信息系統,視圖的做用更加劇要。這些系統的基本表完成物理設計以後,當即在基本表上創建第一層視圖,這層視圖的個數和結構,與基本表的個數和結構是徹底相同。而且規定,全部的程序員,一概只准在視圖上操做。只有數據庫管理員,帶着多我的員共同掌握的「安全鑰匙」,才能直接在基本表上操做。請讀者想一想:這是爲何?

 

·11. 中間表、報表和臨時表

中間表是存放統計數據的表,它是爲數據倉庫、輸出報表或查詢結果而設計的,有時它沒有主鍵與外鍵(數據倉庫除外)。臨時表是程序員我的設計的,存放臨時記錄,爲我的所用。基表和中間表由DBA維護,臨時表由程序員本身用程序自動維護。

 

·12. 完整性約束表如今三個方面

域的完整性:用Check來實現約束,在數據庫設計工具中,對字段的取值範圍進行定義時,有一個Check按鈕,經過它定義字段的值城。參照完整性:用PK、FK、表級觸發器來實現。用戶定義完整性:它是一些業務規則,用存儲過程和觸發器來實現。

 

·13. 防止數據庫設計打補丁的方法是「三少原則」

(1) 一個數據庫中表的個數越少越好。只有表的個數少了,才能說明系統的E--R圖少而精,去掉了重複的多餘的實體,造成了對客觀世界的高度抽象,進行了系統的數據集成,防止了打補丁式的設計;

(2) 一個表中組合主鍵的字段個數越少越好。由於主鍵的做用,一是建主鍵索引,二是作爲子表的外鍵,因此組合主鍵的字段個數少了,不只節省了運行時間,並且節省了索引存儲空間;

(3) 一個表中的字段個數越少越好。只有字段的個數少了,才能說明在系統中不存在數據重複,且不多有數據冗餘,更重要的是督促讀者學會「列變行」,這樣就防止了將子表中的字段拉入到主表中去,在主表中留下許多空餘的字段。所謂「列變行」,就是將主表中的一部份內容拉出去,另外單獨建一個子表。這個方法很簡單,有的人就是不習慣、不採納、不執行。

數據庫設計的實用原則是:在數據冗餘和處理速度之間找到合適的平衡點。「三少」是一個總體概念,綜合觀點,不能孤立某一個原則。該原則是相對的,不是絕對的。「三多」原則確定是錯誤的。試想:若覆蓋系統一樣的功能,一百個實體(共一千個屬性) 的E--R圖,確定比二百個實體(共二千個屬性) 的E--R圖,要好得多。

提倡「三少」原則,是叫讀者學會利用數據庫設計技術進行系統的數據集成。數據集成的步驟是將文件系統集成爲應用數據庫,將應用數據庫集成爲主題數據庫,將主題數據庫集成爲全局綜合數據庫。集成的程度越高,數據共享性就越強,信息孤島現象就越少,整個企業信息系統的全局E—R圖中實體的個數、主鍵的個數、屬性的個數就會越少。

提倡「三少」原則的目的,是防止讀者利用打補丁技術,不斷地對數據庫進行增刪改,使企業數據庫變成了隨意設計數據庫表的「垃圾堆」,或數據庫表的「大雜院」,最後形成數據庫中的基本表、代碼表、中間表、臨時表雜亂無章,不可勝數,致使企事業單位的信息系統沒法維護而癱瘓。

「三多」原則任何人均可以作到,該原則是「打補丁方法」設計數據庫的歪理學說。「三少」原則是少而精的原則,它要求有較高的數據庫設計技巧與藝術,不是任何人都能作到的,由於該原則是杜絕用「打補丁方法」設計數據庫的理論依據。

 

·14. 提升數據庫運行效率的辦法

在給定的系統硬件和系統軟件條件下,提升數據庫系統的運行效率的辦法是:

(1) 在數據庫物理設計時,下降範式,增長冗餘, 少用觸發器, 多用存儲過程。

(2) 當計算很是複雜、並且記錄條數很是巨大時(例如一千萬條),複雜計算要先在數據庫外面,以文件系統方式用C++語言計算處理完成以後,最後才入庫追加到表中去。這是電信計費系統設計的經驗。

(3) 發現某個表的記錄太多,例如超過一千萬條,則要對該表進行水平分割。水平分割的作法是,以該表主鍵PK的某個值爲界線,將該表的記錄水平分割爲兩個表。若發現某個表的字段太多,例如超過八十個,則垂直分割該表,將原來的一個表分解爲兩個表。

(4) 對數據庫管理系統DBMS進行系統優化,即優化各類系統參數,如緩衝區個數。

(5) 在使用面向數據的SQL語言進行程序設計時,儘可能採起優化算法。

總之,要提升數據庫的運行效率,必須從數據庫系統級優化、數據庫設計級優化、程序實現級優化,這三個層次上同時下功夫。

 

上述十四個技巧,是許多人在大量的數據庫分析與設計實踐中,逐步總結出來的。對於這些經驗的運用,讀者不能生幫硬套,死記硬背,而要消化理解,實事求是,靈活掌握。並逐步作到:在應用中發展,在發展中應用。

 

本文來自CSDN博客,轉載請標明出處:

http://blog.csdn.net/lovegod12/archive/2009/03/13/3986346.aspx

 

 

 

 

 

 

SQL數據庫建表前期優化

關於數據庫優化方面的文章不少,可是有的寫的似是而非,有的不切實際,對一個數據庫來講,只能作到更優,不可能最優,而且因爲實際需求不一樣,優化方案仍是有所差別,根據實際須要關心的方面(速度、存儲空間、可維護性、可拓展性)來優化數據庫,而這些方面每每又是相互矛盾的,下面結合網上的一些見解和本身的一些觀點作個總結。

  一個系統的性能的提升,不僅僅是試運行或者維護階段的性能調優,也不僅僅是開發階段的事情,而是在整個軟件生命週期都須要注意。因此我但願按照軟件生命週期的不一樣階段來總結數據庫性能優化相關的注意事項。

 

  1、 分析階段

 

  通常來講,在系統分析階段每每有太多須要關注的地方,系統各類功能性、可用性、可靠性、安全性需求每每吸引了咱們大部分的注意力,可是,咱們必須注意,性能是很重要的非功能性需求,必須根據系統的特色肯定其實時性需求、響應時間的需求、硬件的配置等。最好能有各類需求的量化的指標。

  另外一方面,在分析階段應該根據各類需求區分出系統的類型,大的方面,區分是OLTP(聯機事務處理系統)和OLAP(聯機分析處理系統)。

 

  2、 設計階段

 

  設計階段能夠說是之後系統性能的關鍵階段,在這個階段,有一個關係到之後幾乎全部性能調優的過程—數據庫設計。

  在數據庫設計完成後,能夠進行初步的索引設計,好的索引設計能夠指導編碼階段寫出高效率的代碼,爲整個系統的性能打下良好的基礎。

  如下是性能要求設計階段須要注意的:

  一、數據庫邏輯設計的規範化

  數據庫邏輯設計的規範化就是咱們通常所說的範式,咱們能夠這樣來簡單理解範式:

  第1規範:沒有重複的組或多值的列,這是數據庫設計的最低要求。

第2規範: 每一個非關鍵字段必須依賴於主關鍵字,不能依賴於一個組合式主關鍵字的某些組成部分。消除部分依賴,大部分狀況下,數據庫設計都應該達到第二範式。

  第3規範: 一個非關鍵字段不能依賴於另外一個非關鍵字段。消除傳遞依賴,達到第三範式應該是系統中大部分表的要求,除非一些特殊做用的表。

  更高的範式要求這裏就再也不做介紹了,我的認爲,若是所有達到第二範式,大部分達到第三範式,系統會產生較少的列和較多的表,於是減小了數據冗餘,也利於性能的提升。

  二、合理的冗餘

  徹底按照規範化設計的系統幾乎是不可能的,除非系統特別的小,在規範化設計後,有計劃地加入冗餘是必要的。

  冗餘能夠是冗餘數據庫、冗餘表或者冗餘字段,不一樣粒度的冗餘能夠起到不一樣的做用。

  冗餘能夠是爲了編程方便而增長,也能夠是爲了性能的提升而增長。從性能角度來講,冗餘數據庫能夠分散數據庫壓力,冗餘表能夠分散數據量大的表的併發壓力,也能夠加快特殊查詢的速度,冗餘字段能夠有效減小數據庫表的鏈接,提升效率。

  三、主鍵的設計

  主鍵是必要的,SQLSERVER的主鍵同時是一個惟一索引,並且在實際應用中,咱們每每選擇最小的鍵組合做爲主鍵,因此主鍵每每適合做爲表的彙集索引。彙集索引對查詢的影響是比較大的,這個在下面索引的敘述。

  在有多個鍵的表,主鍵的選擇也比較重要,通常選擇總的長度小的鍵,小的鍵的比較速度快,同時小的鍵能夠使主鍵的B樹結構的層次更少。

  主鍵的選擇還要注意組合主鍵的字段次序,對於組合主鍵來講,不一樣的字段次序的主鍵的性能差異可能會很大,通常應該選擇重複率低、單獨或者組合查詢可能性大的字段放在前面。

  四、外鍵的設計

外鍵做爲數據庫對象,不少人認爲麻煩而不用,實際上,外鍵在大部分狀況下是頗有用的,理由是:

  外鍵是最高效的一致性維護方法,數據庫的一致性要求,依次能夠用外鍵、CHECK約束、規則約束、觸發器、客戶端程序,通常認爲,離數據越近的方法效率越高。

  謹慎使用級聯刪除和級聯更新,級聯刪除和級聯更新做爲SQL SERVER 2000當年的新功能,在2005做 了保留,應該有其可用之處。我這裏說的謹慎,是由於級聯刪除和級聯更新有些突破了傳統的關於外鍵的定義,功能有點太過強大,使用前必須肯定本身已經把握好其功能範圍,不然,級聯刪除和級聯更新可能讓你的數據莫名其妙的被修改或者丟失。從性能看級聯刪除和級聯更新是比其餘方法更高效的方法。

  五、字段的設計

  字段是數據庫最基本的單位,其設計對性能的影響是很大的。須要注意以下:

  A、數據類型儘可能用數字型,數字型的比較比字符型的快不少。

  B、數據類型儘可能小,這裏的儘可能小是指在知足能夠預見的將來需求的前提下的。

  C、儘可能不要容許NULL,除非必要,能夠用NOT NULL+DEFAULT代替。

  D、少用TEXT和IMAGE,二進制字段的讀寫是比較慢的,並且,讀取的方法也很少,大部分狀況下最好不用。

  E、自增字段要慎用,不利於數據遷移。

  六、數據庫物理存儲和環境的設計

  在設計階段,能夠對數據庫的物理存儲、操做系統環境、網絡環境進行必要的設計,使得咱們的系統在未來能適應比較多的用戶併發和比較大的數據量。

  這裏須要注意文件組的做用,適用文件組能夠有效把I/O操做分散到不一樣的物理硬盤,提升併發能力。

  七、系統設計

  整個系統的設計特別是系統結構設計對性能是有很大影響的,對於通常的OLTP系統,能夠選擇C/S結構、三層的C/S結構等,不一樣的系統結構其性能的關鍵也有所不一樣。

系統設計階段應該概括一些業務邏輯放在數據庫編程實現,數據庫編程包括數據庫存儲過程、觸發器和函數。用數據庫編程實現業務邏輯的好處是減小網絡流量並可更充分利用數據庫的預編譯和緩存功能。

  八、索引的設計

  在設計階段,能夠根據功能和性能的需求進行初步的索引設計,這裏須要根據預計的數據量和查詢來設計索引,可能與未來實際使用的時候會有所區別。

  關於索引的選擇,應該主意:

  A、根據數據量決定哪些表須要增長索引,數據量小的能夠只有主鍵。

  B、根據使用頻率決定哪些字段須要創建索引,選擇常常做爲鏈接條件、篩選條件、聚合查詢、排序的字段做爲索引的候選字段。字段(列)容許爲空通常來講不創建索引。

  C、把常常一塊兒出現的字段組合在一塊兒,組成組合索引,組合索引的字段順序與主鍵同樣,也須要把最經常使用的字段放在前面,把重複率低的字段放在前面。

D、一個表不要加太多索引,由於索引影響插入和更新的速度。

本篇文檔來自:

http://hi.baidu.com/zhnantz/blog/item/2349a1019be1a9c7277fb5b5.html


 

SQL數據庫操做優化

1.應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null

能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢:

select id from t where num=0

2.應儘可能避免在 where 子句中使用!=或<>操做符,不然將引擎放棄使用索引而進行全表掃描。優化器將沒法經過索引來肯定將要命中的行數,所以須要搜索該表的全部行。

3.應儘可能避免在 where 子句中使用 or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or num=20

能夠這樣查詢:

select id from t where num=10

union all

select id from t where num=20

4.in和 not in 也要慎用,由於IN會使系統沒法使用索引,而只能直接搜索表中的數據。如:

select id from t where num in(1,2,3)

對於連續的數值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

5.儘可能避免在索引過的字符數據中,使用非打頭字母搜索。這也使得引擎沒法利用索引。

見以下例子:

SELECT * FROM T1 WHERE NAME LIKE ‘%L%’  --搜索含有L的

SELECT * FROM T1 WHERESUBSTING(NAME,2,1)=’L’

SELECT * FROM T1 WHERE NAME LIKE ‘L%’    --搜索開頭是L的

即便NAME字段建有索引,前兩個查詢依然沒法利用索引完成加快操做,引擎不得不對全表全部數據逐條操做來完成任務。而第三個查詢可以使用索引來加快操做。

6.必要時強制查詢優化器使用某個索引,如在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:

select id from t where num=@num

能夠改成強制查詢使用索引:

select id from t with(index(索引名)) where num=@num

7.應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。儘可能使用列名=表達式。如:

SELECT * FROM T1 WHERE F1/2=100

應改成:

SELECT * FROM T1 WHERE F1=100*2

 

SELECT * FROM RECORD WHERESUBSTRING(CARD_NO,1,4)=’5378’

應改成:

SELECT * FROM RECORD WHERE CARD_NO LIKE‘5378%’

 

SELECT member_number, first_name, last_nameFROM members

WHERE DATEDIFF(yy,datofbirth,GETDATE())> 21

應改成:

SELECT member_number, first_name, last_nameFROM members

WHERE dateofbirth <DATEADD(yy,-21,GETDATE())

即:任何對列的操做都將致使表掃描,它包括數據庫函數、計算表達式等等,查詢時要儘量將操做移至等號右邊。

8.應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如:

select id from t wheresubstring(name,1,3)='abc'--name以abc開頭的id

select id from t wheredatediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

應改成:

select id from t where name like 'abc%'

select id from t wherecreatedate>='2005-11-30' and createdate<'2005-12-1'

9.不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。

10.在使用索引字段做爲條件時,若是該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。

11.不少時候用 exists是一個好的選擇,儘可能用exists代替in:

select num from a where num in(select numfrom b)

用下面的語句替換:

select num from a where exists(select 1from b where num=a.num)

 

SELECT SUM(T1.C1)FROM T1 WHERE(

(SELECT COUNT(*)FROM T2 WHERET2.C2=T1.C2>0)

用下面的語句替換:

SELECT SUM(T1.C1) FROM T1WHERE EXISTS(

SELECT * FROM T2 WHERE T2.C2=T1.C2)

二者產生相同的結果,可是後者的效率顯然要高於前者。由於後者不會產生大量鎖定的表掃描或是索引掃描。

若是你想校驗表裏是否存在某條紀錄,不要用count(*)那樣效率很低,並且浪費服務器資源。能夠用EXISTS代替。如:

IF (SELECT COUNT(*) FROM table_name WHEREcolumn_name = 'xxx')

能夠寫成:

IF EXISTS (SELECT * FROM table_name WHEREcolumn_name = 'xxx')

常常須要寫一個T_SQL語句比較一個父結果集和子結果集,從而找到是否存在在父結果集中有而在子結果集中沒有的記錄,如:

SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用別名a代替

WHERE NOT EXISTS (SELECT * FROM dtl_tbl bWHERE a.hdr_key = b.hdr_key)

SELECT a.hdr_key FROM hdr_tbl a

LEFT JOIN dtl_tbl b ON a.hdr_key =b.hdr_key WHERE b.hdr_key IS NULL

SELECT hdr_key FROM hdr_tbl

WHERE hdr_key NOT IN (SELECT hdr_key FROMdtl_tbl)

三種寫法均可以獲得一樣正確的結果,可是效率依次下降。

效率:exists>inner(left,right)join>in

12.儘可能使用表變量來代替臨時表。若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)。

13.避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。

14.臨時表並非不可以使用,適當地使用它們能夠使某些例程更有效,例如,當須要重複引用大型表或經常使用表中的某個數據集時。可是,對於一次性事件,最好使用導出表。

15.在新建臨時表時,若是一次性插入數據量很大,那麼能夠使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先createtable,而後insert。

16.若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table TableName,而後 drop tableTableName,這樣能夠避免系統表的較長時間鎖定。

17.在全部的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。

18.儘可能避免大事務操做,提升系統併發能力。

19.儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

20. 避免使用不兼容的數據類型。例如float和int、char和varchar、binary和varbinary是不兼容的。數據類型的不兼容可能使優化器沒法執行一些原本能夠進行的優化操做。例如:

SELECTname FROM employee WHERE salary > 60000

在這條語句中,如salary字段是money型的,則優化器很難對其進行優化,由於60000是個整型數。咱們應當在編程時將整型轉化成爲錢幣型,而不要等到運行時轉化。

21.充分利用鏈接條件,在某種狀況下,兩個表之間可能不僅一個的鏈接條件,這時在 WHERE 子句中將鏈接條件完整的寫上,有可能大大提升查詢速度。

例:

SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD BWHERE A.CARD_NO = B.CARD_NO

SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD BWHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO

第二句將比第一句執行快得多。

2二、使用視圖加速查詢

把表的一個子集進行排序並建立視圖,有時能加速查詢。它有助於避免多重排序操做,並且在其餘方面還能簡化優化器的工做。例如:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id =rcvlbes.customer_id

AND rcvblls.balance>0

AND cust.postcode>「98000」

ORDER BY cust.name

若是這個查詢要被執行屢次而不止一次,能夠把全部未付款的客戶找出來放在一個視圖中,並按客戶的名字進行排序:

CREATE VIEW DBO.V_CUST_RCVLBES

AS

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id =rcvlbes.customer_id

AND rcvblls.balance>0

ORDER BY cust.name

而後如下面的方式在視圖中查詢:

SELECT * FROM V_CUST_RCVLBES

WHERE postcode>「98000」

視圖中的行要比主表中的行少,並且物理順序就是所要求的順序,減小了磁盤I/O,因此查詢工做量能夠獲得大幅減小。

2三、能用DISTINCT的就不用GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice> 10 GROUP BY OrderID

可改成:

SELECT DISTINCT OrderID FROM Details WHEREUnitPrice > 10

24.能用UNION ALL就不要用UNION

UNION ALL不執行SELECT DISTINCT函數,這樣就會減小不少沒必要要的資源。 

25.儘可能不要用SELECT INTO語句。

SELECTINTO 語句會致使表鎖定,阻止其餘用戶訪問該表。

 

上面咱們提到的是一些基本的提升查詢速度的注意事項,可是在更多的狀況下,每每須要反覆試驗比較不一樣的語句以獲得最佳方案。最好的方法固然是測試,看實現相同功能的SQL語句哪一個執行時間最少,可是數據庫中若是數據量不多,是比較不出來的,這時能夠用查看執行計劃,即:把實現相同功能的多條SQL語句考到查詢分析器,按CTRL+L看查所利用的索引,表掃描次數(這兩個對性能影響最大),整體上看詢成本百分比便可。

http://www.cnblogs.com/yongheng178/archive/2010/09/18/1829989.html

 


 

數據庫命名規範

表1. 基本數據庫對象命名

數據庫對象

前綴

舉例

表(Table)

Student

字段、列(Column)

Title,CustomerName

視圖(View)

v或vw

vActivity,VW_Car

存儲過程(Stored procedure)

pr或proc

prDelOrder

觸發器(Trigger)

tr或trig

trOrder_D

索引(Index)

ix_

ix_CustomerID

主鍵(Primary key)

PK_

PK_Admin

外鍵(Foreign key)

Fk_

FK_Order_OrderType

Check約束(Check Constraint)

CH_

CK_TableColumn

Unique約束

UQ_

UQ_TableColumn

Default默認值

DF_

DF_Status

用戶定義數據類型(User-defined data type)

udt

udtPhone

用戶定義函數(User-defined function)

FN或Fun_

fnDueDate

 

(1)表格、字段的命名:

單數表名、字段名 仍是 複數表名、字段名  (用單數來命名錶名)?可能你們不多會考慮到給表名起單數仍是複數,好比,對存儲客人信息的表,咱們應該起Customer,仍是Customers?我主張起單數表名,下面是來自《SQL Server 2000 寶典》的一段引用:主張用複數表名的陣營認爲:表是由一組記錄構成的,因此應當使用複數名詞來命名它。他們常用的理由是:客戶表是客戶們的集合,而集合意味着多個,所以應當稱他們爲Customers表。除非你只有一個客戶,但這種狀況你根本用不着數據庫。根據筆者的非正式調查,有3/4的SQL Server開發人員支持使用單數命名。這些開發人員認爲,客戶表是客戶的集合,而不是客戶們的集合。一組行不該當也不會被成爲rows set(行們的集合),而會被稱爲row set(行集)。而且,一般在討論時人們會使用單數名稱來稱呼表,說Customer表比說Customers表聽起來更爲清晰,避免無謂的表格後綴(不必添加無所謂的後綴)。特殊狀況下能夠加複數,如用戶表User,而User是數據庫關鍵字,若是使用User表的時候,通常這樣select * from [User]。此時最好使用Users

這兩點我想你們都知道:一、表是用來存儲數據信息的。二、表是行的集合。那麼若是表名已經可以很好地說明其包含的數據信息,就不須要再添加體現上面兩點的後綴了。

(2)多對多關係中鏈接表(中間表)的命名

你們知道,若是要實現兩個實體間的多對多關係,須要三張表,其中一張是解析表。考慮下面這樣一個多對多關係,這是一個經典的學生選課問題:一個學生能夠選不少門課,一門課能夠有不少學生。此時爲了實現上面的關係,就須要一張解析表(這張表只存儲學生ID和課程ID,而學生的信息和課程信息分別存在各自的表中),這個表的起名,建議的寫法是將兩個表的表名合併(若是表名比較長可作簡化),此處如 StudentCourse。這個表中字段分別命名爲StudentId、CourseID(既是此表的複合主鍵,同時分別爲鏈接Student表和Course表的外鍵,等下到主鍵和外鍵的命名處再說),這樣就實現了學生和課程之間的多對多關係,固然,這個關係還能夠加點額外的東西,好比給StudentCourse表中加AccessLevel字段,值域D{只讀,徹底,禁止},就能夠實現訪問級別。

(3)約定俗成的字段名前/後綴

數據庫開發的時間久了,慢慢就會摸索出一個規律來:就是不少的字段都有些共同的特性。好比說,有的字段是表明時間的(例如發帖時間,評論時間),有的是表明數量的(例如瀏覽數,評論數),有的是表明真假類型的(例如是否將博客隨筆顯示在首頁)。對於這種同一類型的字段,應該使用統一的 前綴或者 後綴去標識它。

咱們來舉幾個例子看得更明白一點。

以你們都熟悉的論壇來講,須要記錄會員最後一次登陸的時間,這時候通常人都會把這個字段命名爲LoginTime 或者 LoginDate。這時候,已經產生了一個歧義:對於另外一名開發者來講,若是僅看錶的字段名稱,不去看錶的內容,很容易將LoginTime理解成 登陸的次數,由於,Time還有一個很經常使用的意思,就是次數。爲了不這種狀況發生,應該明確的規定:全部表示時間的字段,統一以 Date 來做爲結尾。咱們常常須要統計發帖數、回帖數信息,這時候,開發人員一般會這樣去命名字段:PostAmount、PostTime、PostCount,一樣,因爲Time的歧義,咱們首先排除掉不使用PostTime做爲字段名。接下來,Amount 和 Count 均可以表示計數的意思,用哪一個合適呢?這裏,我推薦使用Count。爲何呢?若是你作過Asp開發,相信必定知道 RecordCount 這個屬性,命名的時候有一個原則:就是使用約定俗成的名稱,而不要去自創名稱。既然微軟都用Count後綴來表示數目,咱們爲何不呢?

因而,全部表示數目的字段,都應該以Count做爲結尾。將這一律唸作以推廣,很容易得出,瀏覽次數爲 ViewCount,登陸次數爲LoginCount 等等。再舉一個例子,咱們不多在數據庫裏直接保存圖片等二進制數據,一般是僅保存圖片的URL路徑;在文章管理系統中,若是是轉載文章,也會用到記錄文章出處的字段。我的建議全部表明連接的字段,均爲Url結尾。因而,圖片路徑的字段命名爲 ImageUrl,文章出處字段的命名爲SourceUrl。

最後一個例子,咱們常常須要用到布爾值,比方說,這篇隨筆要不要顯示到首頁,這篇隨筆是否是保存到草稿箱等等。一樣,按照微軟的建議,布爾類型的值均以 Is、Has、Exist 或者 Can開頭。若是讓我來建表示是否將隨筆放到首頁的字段,它的名字必定是這樣的:IsOnIndex

(4)字段命名時需注意的一個問題

我發現有不少開發人員喜歡給字段加上表名做爲它的前綴,舉個例子,若是有個表叫User,那麼他就會將這個表中的字段命名爲:UserId、UserPassword、UserName、UserPhone 等等。我的認爲,這是沒有必要的,由於你已經確切的知道了這個表存儲的是User的信息,那麼其中的字段必然是針對於User的。並且,在Join鏈接操做中,你的SQL代碼看上去也會更加的精簡一些,諸如 [User].UserName =Aritcle.ArticleAuthor 這樣的代碼徹底能夠實現爲 [User].Name = Article.Author。(不必添加無所謂的後綴)

這裏還存在一個特例,就是表的外鍵包含的字段。在這種狀況下,我傾向於使用表名+ID 的方式,好比 CategoryId、UserId 等。假設有表Article,那麼它的主鍵我會命名爲Id,關聯用戶表User的外鍵包含的字段,我會命名爲UserId。之因此這樣,是由於在語言(好比C#)中建立對象時,有時候會使用代碼生成器(根據數據庫的字段名生成對象的字段、屬性名),此時生成的代碼更規整一些。(對於外鍵要用到,外表名+Id)

(5)外鍵的命名

外鍵的命名爲 fk_外鍵所在的表名_外鍵引用的表名。由於外鍵所在的表爲從表,因此上式能夠寫爲 fk_從表名_主表名。外鍵包含的字段的命名,外鍵包含的字段和外鍵是徹底不一樣的概念。外鍵包含字段的命名,建議爲:外鍵所在的表名 + Id。考慮這樣一個關係,表Hotel,字段Id, Name, CityId。表City,字段Id,Name。由於一個城市可能有好多家酒店,因此是一個一對多的關係,City是主表(1方),Hotel是從表(多方)。在Hotel表中,CityId是作爲外鍵使用。在實現外鍵的時候咱們能夠這樣寫:

Alter Table HotelInfo

Add Constraint fk_HotelInfo_City ForeignKey (CityID) References City(ID)

On Delete No Action On update No Action

很明顯,fk_HotelInfo_City是外鍵的名字,CityId是外鍵包含的字段的名字。

在建立數據庫表的時候,通常須要寫成三個SQL腳本文件。第一個文件僅包含全部的建立表的SQL語句,即CreateTable 語句。第二個文件包含刪除關係和表的語句,其中,全部刪除關係的語句,即Drop Constraint 語句集中在這個文件的上半部分,全部刪除表的語句,Drop Table語句,集中在這個文件的下半部分。第三個文件包含創建表之間關係的語句。這種作法會在你移植數據庫的時候產生較大的便利,緣由我就不解釋了,您一試便知。而對於多對多關係中解析表的外鍵包含的字段,順理往下推,咱們能夠這樣寫(再次回到學生選課的多對多例子中):

創建解析表StudentCourse與Student表的外鍵關係:

Alter Table StudentCourse

Add Constraint fk_StudentCourse_StudentForeign Key (StudentId) References Student (Id)

On Delete No Action On Update No Action

創建解析表StudentCourse與Course 表的外鍵關係:

Alter Table StudentCourse

Add Constraint fk_StudentCourse_CourseForeign Key (CourseId) References Course(Id)

On Delete No Action On Update No Action

(6)以Not Null的思路建表

我發現不少開發人員在建表的時候,若是要新建一個字段,他的思路是這樣的:默認這個字段是能夠爲Null的,而後去判斷是否是非要NotNull不可,若是不是這樣,OK,這個字段能夠爲Null,接着繼續進行下一個字段。結果每每是一張表除了主鍵之外全部的字段均可覺得Null。之因此會有這樣的思路,是由於Null好啊,程序不容易出錯啊,你插入記錄的時候若是不當心忘輸了一個字段,程序依然能夠Run,而不會出現 「XX字段不能爲Null」的錯誤消息。可是,這樣作的結果倒是很嚴重的,也會使你的程序變得更加繁瑣,你不得不進行一些無謂的空值處理,以免程序出錯。更糟的是,若是一些重要數據,好比說訂單的某一項值爲Null了,那麼你們知道,任何值與Null相操做(好比加減乘除),結果都是Null,致使的結果就是訂單的總金額也爲Null。

你能夠運行下面的代碼嘗試一下:

Select Null + 5 As Result

你可能會說,就算我將字段設置成Not Null,可是它依然能夠接受空字符串,這樣一來在程序中仍是要進行空值處理。請別忘了,數據庫還賦予你一個強力武器,就是 Check 約束,當你須要確保一個字段既不能夠爲Null,又不能夠爲空的時候,能夠這麼寫:

ColumnName    Varchar(50)       Not Null Constraint ck_ColumnNameCheck(Len(ColumnName) > 0)

因此,合理的思惟方式應該是這樣的:默認這個字段是 Not Null的,而後判斷這個字段是否是非爲Null不可,若是不是這樣,OK,這個字段是Not Null的,進行下一個字段。

一個建表的範例腳本個建表的範例腳本

我正在創建我本身的我的空間,其中的文章表是這樣寫的:

Create Table Article

(

    Id           Int Identity(1,1) Not Null,

   Title         Varchar(50)       Not Null Constraint uq_ArticleTitleUnique,

   Keywords      Varchar(50)       Not Null,

   Abstract      Varchar(500)      Not Null,

   Author        Varchar(50)       Not Null Default '張三',

   Type          TinyInt           Not Null Default 0 Constraintck_ArticleType Check(Type in (0,1,2)), -- 0,原創;1,編譯;2,翻譯

   IsOnIndex     Bit               Not Null Default 1,   -- 是否顯示在首頁

   Content       Text              Not Null,

   SourceCode    Varchar(100)      Null, -- 程序源碼的下載路徑

   Source        Varchar(50)       Not Null Default 'TraceFact',   -- 文章出處

   SrcUrl        Varchar(150)      Null, -- 文章出處的URL

   PostDate      DateTime          Not Null Default GetDate(),

    ViewCount     Int               Not Null Default 0,

   ClassId       Int               Not Null   -- 外鍵包含的字段,文章類別

   Constraint pk_Article Primary Key(Id)  -- 創建主鍵

)

能夠看到,在這裏我使用了Check 約束,以確保文章類型只能爲0,1,2。這裏,我想說的是Check約束的命名規則:儘管Check約束是針對字段的,但在同一數據庫中,卻不能有同名的Check約束。因此,建議使用 ck_ + 表名 + 字段名來命名它,好比這個範例腳本中的 ck_ArticleType。除此之外,我還使用了Unique約束,以確保文章標題的惟一性。因爲這是個人博客文章表,不該該出現重複的題目,這樣能夠避免在使用 Insert 語句時插入重複值。相似於Check約束,這裏的命名規則是:uq_+ 表名 + 字段名。

(7)觸發器的命名

由三部分構成:

前綴(tr),描述了數據庫對象的類型。

基本部分,描述觸發器所加的表。

後綴(_I、_U、_D),顯示了修改語句(Insert,Update及Delete)

(8)存儲過程的命名

你們知道,系統存儲過程的前綴是 sp_,爲了不將用戶存儲過程與系統存儲過程混淆,這裏我推薦你們使用 pr 做爲本身定義的存儲過程的命名。

同時,命名的規則是:採用自解釋型的命名,好比:prGetItemById。

這裏,有個有意思的地方值得深思。咱們按上面規則命名存儲過程的時候,能夠用兩種方式:

動詞放前面,名詞放後面。

名詞放前面,動詞放後面。

我我的推薦使用方式2,如今說說緣由:

以NorthWind 爲例,假如對於 Employees 表你有4個存儲過程,分別命名爲:prEmployeeInsert、prEmployeeUpdate、prEmployeeDelById、prEmployeeGetById

同時對於 Products 表你有相似的4個存儲過程,分別命名爲:prProductInsert、prProductUpdate、prProductDelById、prProductGetById

這時,你用企業管理器查看時,會發現存儲過程像下面這樣整整齊齊的排列:

prEmployeeDelById

prEmployeeGetById

prEmployeeInsert

prEmployeeUpdate

prProductDelById

prProductGetById

prProductInsert

prProductUpdate

很容易就會發現,當你的存儲過程越多時,這種命名方法的優點就越明顯。

(9)存儲過程當中參數的命名

存儲過程當中的入口參數,我建議與其對應的字段名相同,這裏,假設要寫一個更新Northwind數據庫Employees表的存儲過程(作了簡化),能夠這麼寫:

Create Procedure prEmployeeUpdateById

   @EmployeeId       Int,

   @LastName     NVarchar(20),

   @FirstName    NVarchar(10)

As

   Update Employees Set

      LastName = @LastName,

      FirstName = @FirstName

   Where

      EmployeeId = @EmployeeId

   If @@error <> 0 or @@RowCount = 0

      Raiserror 16001 ‘更新用戶失敗’

總結:

(Id統一這樣命名不進行ID獲取id這樣的命名)

1.數據庫表的命名:用頭個字母大寫的方式進行命名,對於有多個單詞組成的在適當看具體狀況進行裁剪。

2.對於字段命名,採起和表命名同樣的規則,我的習慣的命名規則是:表名+字段名。(本身以爲能夠容許這樣適當的冗餘)

3.外鍵命名:外表名+Id

4.對於存儲過程的命名遵循上面所說的原則,pr+名詞(一般指表名)+動詞操做。

5.對於存儲過程當中參數的命名:和該存儲過程所做用的數據表的相關字段同樣也就是在其前面加一個@。對於存儲過程當中相應臨時參數的命名也是同樣的,用頭個字母大寫的方式進行命名。

6.對於觸發器的命名:用tr前綴:tr+所做用的表名+After/Before(I/D/U) trProductAfterI

相關文章
相關標籤/搜索