經常使用 SQL Server 規範集錦

轉自博客:http://www.cnblogs.com/liyunhua/p/4534442.html#tophtml

常見的字段類型選擇

1.字符類型建議採用varchar/nvarchar數據類型
2.金額貨幣建議採用money數據類型
3.科學計數建議採用numeric數據類型
4.自增加標識建議採用bigint數據類型   (數據量一大,用int類型就裝不下,那之後改造就麻煩了)
5.時間類型建議採用爲datetime數據類型
6.禁止使用text、ntext、image老的數據類型
7.禁止使用xml數據類型、varchar(max)、nvarchar(max)

約束與索引

每張表必須有主鍵sql

•每張表必須有主鍵,用於強制實體完整性數據庫

 

•單表只能有一個主鍵(不容許爲空及重複數據)服務器

•儘可能使用單字段主鍵網絡

 

不容許使用外鍵數據結構

•外鍵增長了表結構變動及數據遷移的複雜性
•外鍵對插入,更新的性能有影響,須要檢查主外鍵約束
•數據完整性由程序控制

NULL屬性架構

•新加的表,全部字段禁止NULL
新表爲何不容許NULL? 
容許NULL值,會增長應用程序的複雜性。你必須得增長特定的邏輯代碼,以防止出現各類意外的bug
三值邏輯,全部等號(「=」)的查詢都必須增長isnull的判斷。
Null=Null、Null!=Null、not(Null=Null)、not(Null!=Null)都爲unknown,不爲true
舉例來講明一下:
若是表裏面的數據如圖所示:

 

你想來找查找除了name等於aa的全部數據,而後你就不經意間用了

SELECT * FROM NULLTEST WHERE NAME<>’aa’併發

結果發現與預期不同,事實上它只查出了name=bb而沒有查找出name=NULL的數據記錄分佈式

那咱們如何查找除了name等於aa的全部數據,只能用ISNULL函數了函數

SELECT * FROM NULLTEST WHERE ISNULL(NAME,1)<>’aa’

 
可是你們可能不知道 ISNULL會引發很嚴重的性能瓶頸 ,因此不少時候最好是在應用層面限制用戶的輸入,確保用戶輸入有效的數據再進行查詢。
舊錶新加字段,須要容許爲NULL(避免全表數據更新 ,長期持鎖致使阻塞)(這個主要是考慮以前表的改造問題)

索引設計準則

•應該對 WHERE 子句中常用的列建立索引
•應該對常常用於鏈接表的列建立索引
•應該對 ORDER BY 子句中常用的列建立索引
•不該該對小型的表(僅使用幾個頁的表)建立索引,這是由於徹底表掃描操做可能比使用索引執行的查詢快
•單表索引數不超過6個
•不要給選擇性低的字段建單列索引
•充分利用惟一約束
•索引包含的字段不超過5個(包括include列)

不要給選擇性低的字段建立單列索引

•SQL SERVER對索引字段的選擇性有要求,若是選擇性過低SQL SERVER會放棄使用•
•不適合建立索引的字段:性別、0/一、TRUE/FALSE
•適合建立索引的字段:ORDERID、UID等

充分利用惟一索引

惟一索引給SQL Server提供了確保某一列絕對沒有重複值的信息,當查詢分析器經過惟一索引查找到一條記錄則會馬上退出,不會繼續查找索引

表索引數不超過6個

表索引數不超過6個(這個規則只是攜程DBA通過試驗以後制定的。。。)

•索引加快了查詢速度,可是卻會影響寫入性能
•一個表的索引應該結合這個表相關的全部SQL綜合建立,儘可能合併
•組合索引的原則是,過濾性越好的字段越靠前
•索引過多不只會增長編譯時間,也會影響數據庫選擇最佳執行計劃

SQL查詢

禁止在數據庫作複雜運算
•禁止使用SELECT *
•禁止在索引列上使用函數或計算
•禁止使用遊標
•禁止使用觸發器
•禁止在查詢裏指定索引
•變量/參數/關聯字段類型必須與字段類型一致
•參數化查詢
•限制JOIN個數
•限制SQL語句長度及IN子句個數
•儘可能避免大事務操做
•關閉影響的行計數信息返回
•除非必要SELECT語句都必須加上NOLOCK
•使用UNION ALL替換UNION
•查詢大量數據使用分頁或TOP
•遞歸查詢層級限制
•NOT EXISTS替代NOT IN
•臨時表與表變量
•使用本地變量選擇中庸執行計劃
•儘可能避免使用OR運算符
•增長事務異常處理機制
•輸出列使用二段式命名格式

 

禁止在數據庫作複雜運算

•XML解析
•字符串類似性比較
•字符串搜索(Charindex)
•複雜運算在程序端完成

禁止使用SELECT *

•減小內存消耗和網絡帶寬
•給查詢優化器有機會從索引讀取所須要的列
•表結構變化時容易引發查詢出錯

禁止在索引列上使用函數或計算

禁止在索引列上使用函數或計算

在where子句中,若是索引是函數的一部分,優化器將再也不使用索引而使用全表掃描 

假設在字段Col1上建有一個索引,則下列場景將沒法使用到索引:

ABS[Col1]=1

[Col1]+1>9

再舉例說明一下

像上面這樣的查詢,將沒法用到O_OrderProcess表上的PrintTime索引,因此咱們應用使用以下所示的查詢SQL

禁止在索引列上使用函數或計算

假設在字段Col1上建有一個索引,則下列場景將可使用到索引:

[Col1]=3.14

[Col1]>100

[Col1] BETWEEN 0 AND 99

[Col1] LIKE ‘abc%’

[Col1] IN(2,3,5,7)

LIKE查詢的索引問題

1.[Col1] like "abc%"  --index seek  這個就用到了索引查詢
2.[Col1] like "%abc%"  --index scan  而這個就並未用到索引查詢
3.[Col1] like "%abc"  --index scan 這個也並未用到索引查詢
我想從上而三個例子中,你們應該明白,最好不要在LIKE條件前面用模糊匹配,不然就用不到索引查詢。

禁止使用遊標

•關係數據庫適合集合操做,也就是對由WHERE子句和選擇列肯定的結果集做集合操做,遊標是提供的一個非集合操做的途徑。通常狀況下,遊標實現的功能每每至關於客戶端的一個循環實現的功能。
•遊標是把結果集放在服務器內存,並經過循環一條一條處理記錄,對數據庫資源(特別是內存和鎖資源)的消耗是很是大的。
(再加上游標真心比較複雜,挺很差用的,儘可能少用吧)

禁止使用觸發器

觸發器對應用不透明(應用層面都不知道會何時觸發觸發器,發生也也不知道,感受莫名......)

禁止在查詢裏指定索引

With(index=XXX)(  在查詢裏咱們指定索引通常都用With(index=XXX)   )

•隨着數據的變化查詢語句指定的索引性能可能並不最佳
•索引對應用應是透明的,如指定的索引被刪除將會致使查詢報錯,不利於排障
•新建的索引沒法被應用當即使用,必須經過發佈代碼才能生效

變量/參數/關聯字段類型必須與字段類型一致(這是我以前不太關注的)

避免類型轉換額外消耗的CPU,引發的大表scan尤其嚴重

看了上面這兩個圖,我想我不用解釋說明,你們都應該已經清楚了吧。

若是數據庫字段類型爲VARCHAR,在應用裏面最好類型指定爲AnsiString並明確指定其長度

若是數據庫字段類型爲CHAR,在應用裏面最好類型指定爲AnsiStringFixedLength並明確指定其長度

若是數據庫字段類型爲NVARCHAR,在應用裏面最好類型指定爲String並明確指定其長度

參數化查詢

如下方式能夠對查詢SQL進行參數化:

•sp_executesql
•Prepared Queries
•Stored procedures
用圖來講明一下,哈哈。

限制JOIN個數

•單個SQL語句的表JOIN個數不能超過5個
•過多的JOIN個數會致使查詢分析器走錯執行計劃
•過多JOIN在編譯執行計劃時消耗很大

限制IN子句中條件個數

•在 IN 子句中包括數量很是多的值(數以千計)可能會消耗資源並返回錯誤 8623 或 8632,要求IN子句中條件個數限制在100個之內

儘可能避免大事務操做

•只在數據須要更新時開始事務,減小資源鎖持有時間
•增長事務異常捕獲預處理機制
•禁止使用數據庫上的分佈式事務
用圖來講明一下
也就是說咱們不該該在1000行數據都更新完成以後再commit tran,你想一想你在更新這一千行數據的時候是否是獨佔資源致使其它事務沒法處理。

關閉影響的行計數信息返回

在SQL語句中顯示設置Set Nocount On,取消影響的行計數信息返回,減小網絡流量

除非必要SELECT語句都必須加上NOLOCK

除非必要,儘可能讓全部的select語句都必須加上NOLOCK

指定容許髒讀。不發佈共享鎖來阻止其餘事務修改當前事務讀取的數據,其餘事務設  置的排他鎖不會阻礙當前事務讀取鎖定數據。容許髒讀可能產生較多的併發操做,但其代價是讀取之後會被其餘事務回滾的數據修改。這可能會使您的事務出錯,向用戶顯示從未提交過的數據,或者致使用戶兩次看到記錄(或根本看不到記錄)

使用UNION ALL替換UNION

使用UNION ALL替換UNION

UNION會對SQL結果集去重排序,增長CPU、內存等消耗

查詢大量數據使用分頁或TOP

合理限制記錄返回數,避免IO、網絡帶寬出現瓶頸

遞歸查詢層次限制

使用 MAXRECURSION 來防止不合理的遞歸 CTE 進入無限循環

臨時表與表變量

使用本地變量選擇中庸執行計劃

在存儲過程或查詢中,訪問了一張數據分佈很不平均的表格,這樣每每會讓存儲過程或查詢使用了次優甚至於較差的執行計劃上,形成High CPU及大量IO Read等問題,使用本地變量防止走錯執行計劃。

採用本地變量的方式,SQL在編譯的時候是不知道這個本地變量的值,這時候SQL會根據表格裏數據的通常分佈,「猜想」一個返回值。無論用戶在調用存儲過程或語句的時候代入的變量值是多少,生成的計劃都是同樣的。這樣的計劃通常會比較中庸一些,不必定是最優的計劃,但通常也不會是最差的計劃

l若是查詢中本地變量使用了不等式運算符,查詢分析器使用了一個簡單的 30% 的算式來預估
Estimated Rows =(Total Rows * 30)/100 
l若是查詢中本地變量使用了等式運算符,則查詢分析器使用:精確度 * 表記錄總數來預估
Estimated Rows = Density * Total Rows 

 

儘可能避免使用OR運算符

對於OR運算符,一般會使用全表掃描,考慮分解成多個查詢用UNION/UNION ALL來實現,這裏要確認查詢能走到索引並返回較少的結果集

增長事務異常處理機制

應用程序作好意外處理,及時作Rollback。
設置鏈接屬性 "set xact_abort on"

輸出列使用二段式命名格式

二段式命名格式:表名.字段名 

有JOIN關係的TSQL,字段必須指明字段是屬於哪一個表的,不然將來表結構變動後,有可能發生Ambiguous column name的程序兼容錯誤

架構設計

讀寫分離
•schema解耦
•數據生命週期

讀寫分離

•設計之初就考慮讀寫分離,哪怕讀寫同一個庫,有利於快速擴容
•按照讀特徵把讀分爲實時讀和可延遲讀分別對應到寫庫和讀庫
•讀寫分離應該考慮在讀不可用狀況下自動切換到寫端

Schema解耦

禁止跨庫JOIN

數據生命週期

根據數據的使用頻繁度,對大表按期分庫歸檔

主庫/歸檔庫物理分離

日誌類型的表應分區或分表

對於大的表格要進行分區,分區操做將表和索引分在多個分區,經過分區切換可以快速實現新舊分區替換,加快數據清理速度,大幅減小IO資源消耗

頻繁寫入的表,須要分區或分表

自增加與Latch Lock 

閂鎖是sql Server本身內部申請和控制,用戶沒有辦法來干預,用來保證內存裏面數據結構的一致性,鎖級別是頁級鎖

相關文章
相關標籤/搜索