SQL SERVER性能優(yōu)化綜述(很好的總結,不要錯過哦)第2/3頁
更新時間:2008年09月30日 23:16:16 作者:
一個系統(tǒng)的性能的提高,不單單是試運行或者維護階段的性能調優(yōu)的任務,也不單單是開發(fā)階段的事情,而是在整個軟件生命周期都需要注意,進行有效工作才能達到的。所以我希望按照軟件生命周期的不同階段來總結數據庫性能優(yōu)化相關的注意事項。
F、
關于臨時表產生使用SELECT INTO和CREATE TABLE + INSERT INTO的選擇,我們做過測試,一般情況下,SELECT INTO會比CREATE TABLE + INSERT INTO的方法快很多,但是SELECT INTO會鎖定TEMPDB的系統(tǒng)表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用戶并發(fā)環(huán)境下,容易阻塞其他進程,所以我的建議是,在并發(fā)系統(tǒng)中,盡量使用CREATE TABLE + INSERT INTO,而大數據量的單個語句使用中,使用SELECT INTO。
G、
注意排序規(guī)則,用CREATE TABLE建立的臨時表,如果不指定字段的排序規(guī)則,會選擇TEMPDB的默認排序規(guī)則,而不是當前數據庫的排序規(guī)則。如果當前數據庫的排序規(guī)則和TEMPDB的排序規(guī)則不同,連接的時候就會出現排序規(guī)則的沖突錯誤。一般可以在CREATE TABLE建立臨時表時指定字段的排序規(guī)則為DATABASE_DEFAULT來避免上述問題。
5、
子查詢的用法
子查詢是一個 SELECT 查詢,它嵌套在 SELECT、INSERT、UPDATE、DELETE 語句或其它子查詢中。任何允許使用表達式的地方都可以使用子查詢。
子查詢可以使我們的編程靈活多樣,可以用來實現一些特殊的功能。但是在性能上,往往一個不合適的子查詢用法會形成一個性能瓶頸。
如果子查詢的條件中使用了其外層的表的字段,這種子查詢就叫作相關子查詢。相關子查詢可以用IN、NOT IN、EXISTS、NOT EXISTS引入。
關于相關子查詢,應該注意:
A、NOT IN、NOT EXISTS的相關子查詢可以改用LEFT JOIN代替寫法。比如:
SELECT PUB_NAMEFROM PUBLISHERS
WHERE PUB_ID NOT IN (SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以改寫成:
SELECT A.PUB_NAMEFROM PUBLISHERS A LEFT JOIN TITLES B
ON B.TYPE = 'BUSINESS' AND A.PUB_ID=B. PUB_ID
WHERE B.PUB_ID IS NULL
SELECT TITLEFROM TITLES
WHERE NOT EXISTS (SELECT TITLE_ID
FROM SALES
WHERE TITLE_ID = TITLES.TITLE_ID)
可以改寫成:
SELECT TITLEFROM TITLES LEFT JOIN SALES
ON SALES.TITLE_ID = TITLES.TITLE_ID
WHERE SALES.TITLE_ID IS NULL
B、
如果保證子查詢沒有重復 ,IN、EXISTS的相關子查詢可以用INNER JOIN 代替。比如:
SELECT PUB_NAMEFROM PUBLISHERS
WHERE PUB_ID IN (SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以改寫成:
SELECT DISTINCT A.PUB_NAMEFROM PUBLISHERS A INNER JOIN TITLES B
ON B.TYPE = 'BUSINESS' AND
A.PUB_ID=B. PUB_ID
C、
IN的相關子查詢用EXISTS代替,比如
SELECT PUB_NAMEFROM PUBLISHERS
WHERE PUB_ID IN (SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以用下面語句代替:
SELECT PUB_NAMEFROM 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
應該改成:
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
應該改成:
SELECT JOB_DESC FROM JOBS
WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)
6、
慎用游標
數據庫一般的操作是集合操作,也就是對由WHERE子句和選擇列確定的結果集作集合操作,游標是提供的一個非集合操作的途徑。一般情況下,游標實現的功能往往相當于客戶端的一個循環(huán)實現的功能,所以,大部分情況下,我們把游標功能搬到客戶端。
游標是把結果集放在服務器內存,并通過循環(huán)一條一條處理記錄,對數據庫資源(特別是內存和鎖資源)的消耗是非常大的,所以,我們應該只有在沒有其他方法的情況下才使用游標。
另外,我們可以用SQL SERVER的一些特性來代替游標,達到提高速度的目的。
A、字符串連接的例子
這是論壇經常有的例子,就是把一個表符合條件的記錄的某個字符串字段連接成一個變量。比如需要把JOB_ID=10的EMPLOYEE的FNAME連接在一起,用逗號連接,可能最容易想到的是用游標:
DECLARE @[url=URL]NAME[/url] VARCHAR(20) DECLARE @NAME VARCHAR(1000)
DECLARE NAME_CURSOR CURSOR FOR SELECT FNAME FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
OPEN NAME_CURSOR FETCH NEXT FROM RNAME_CURSOR INTO @NAME
WHILE @@FETCH_STATUS = 0
BEGIN
SET @NAMES = ISNULL(@NAMES+',','')+@NAME FETCH NEXT FROM NAME_CURSOR INTO @NAME
END CLOSE NAME_CURSOR
DEALLOCATE NAME_CURSOR
可以如下修改,功能相同:
DECLARE @NAME VARCHAR(1000) SELECT @NAMES = ISNULL(@NAMES+',','')+FNAME
FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
B、
用CASE WHEN 實現轉換的例子
很多使用游標的原因是因為有些處理需要根據記錄的各種情況需要作不同的處理,實際上這種情況,我們可以用CASE WHEN語句進行必要的判斷處理,而且CASE WHEN是可以嵌套的。比如:
表結構:
CREATE TABLE 料件表(料號 VARCHAR(30),
名稱 VARCHAR(100),主單位 VARCHAR(20),
單位1 VARCHAR(20),單位1參數 NUMERIC(18,4),
單位2 VARCHAR(20),單位2參數 NUMERIC(18,4)
)
GO
CREATE TABLE 入庫表(時間 DATETIME,
料號 VARCHAR(30),單位 INT,
入庫數量 NUMERIC(18,4),損壞數量 NUMERIC(18,4)
)
GO
其中,單位字段可以是0,1,2,分別代表主單位、單位1、單位2,很多計算需要統(tǒng)一單位,統(tǒng)一單位可以用游標實現:
DECLARE @料號 VARCHAR(30), @單位 INT,
@參數 NUMERIC(18,4),
DECLARE CUR CURSOR FOR SELECT 料號,單位 FROM 入庫表 WHERE 單位 <>0
OPEN CURFETCH NEXT FROM CUR INTO @料號,@單位
WHILE @@FETCH_STATUS<>-1
BEGIN
IF @單位=1
BEGIN
SET @參數=(SELECT 單位1參數 FROM 料件表 WHERE 料號 =@料號) UPDATE 入庫表 SET 數量=數量*@參數,損壞數量=損壞數量*@參數,單位=1 WHERE CURRENT OF CUR
END IF @單位=2
BEGIN SET @參數=(SELECT 單位1參數 FROM 料件表 WHERE 料號 =@料號)
UPDATE 入庫表 SET 數量=數量*@參數,損壞數量=損壞數量*@參數,單位=1 WHERE CURRENT OF CUR END
FETCH NEXT FROM CUR INTO @料號,@單位END
CLOSE CUR
DEALLOCATE CUR
可以改寫成:
UPDATE A SET 數量=CASE A.單位 WHEN 1 THEN A.數量*B. 單位1參數
WHEN 2 THEN A.數量*B. 單位2參數 ELSE A.數量
END, 損壞數量= CASE A.單位 WHEN 1 THEN A. 損壞數量*B. 單位1參數
WHEN 2 THEN A. 損壞數量*B. 單位2參數 ELSE A. 損壞數量
END,單位=1
FROM入庫表 A, 料件表 BWHERE A.單位<>1 AND
A.料號=B.料號
C、
變量參與的UPDATE語句的例子
SQL ERVER的語句比較靈活,變量參與的UPDATE語句可以實現一些游標一樣的功能,比如:
在
SELECT A,B,C,CAST(NULL AS INT) AS 序號INTO #T
FROM 表
ORDER BY A ,NEWID()
產生臨時表后,已經按照A字段排序,但是在A相同的情況下是亂序的,這時如果需要更改序號字段為按照A字段分組的記錄序號,就只有游標和變量參與的UPDATE語句可以實現了,這個變量參與的UPDATE語句如下:
DECLARE @A INTDECLARE @序號 INT
UPDATE #T SET @序號=CASE WHEN A=@A THEN @序號+1 ELSE 1 END,
@A=A,
序號=@序號
D、如果必須使用游標,注意選擇游標的類型,如果只是循環(huán)取數據,那就應該用只進游標(選項FAST_FORWARD),一般只需要靜態(tài)游標(選項STATIC)。
E、
注意動態(tài)游標的不確定性,動態(tài)游標查詢的記錄集數據如果被修改,會自動刷新游標,這樣使得動態(tài)游標有了不確定性,因為在多用戶環(huán)境下,如果其他進程或者本身更改了紀錄,就可能刷新游標的記錄集。
您可能感興趣的文章:
- MySQL 性能優(yōu)化的最佳20多條經驗分享
- MySQL配置文件my.cnf參數優(yōu)化和中文詳解
- MySQL Order by 語句用法與優(yōu)化詳解
- MySQL優(yōu)化必須調整的10項配置
- 淺談MySQL中優(yōu)化sql語句查詢常用的30種方法
- MySQL 百萬級分頁優(yōu)化(Mysql千萬級快速分頁)
- MySQL性能優(yōu)化之max_connections配置參數淺析
- MySQL查詢優(yōu)化之explain的深入解析
- 優(yōu)化Mysql數據庫的8個方法
- sql語句優(yōu)化之用EXISTS替代IN、用NOT EXISTS替代NOT IN的語句
- mysql下優(yōu)化表和修復表命令使用說明(REPAIR TABLE和OPTIMIZE TABLE)
- 淺析Mysql Join語法以及性能優(yōu)化
- MYSQL 優(yōu)化常用方法
- mysql優(yōu)化連接數防止訪問量過高的方法
- MYSQL性能優(yōu)化分享(分庫分表)
- SQLServer 優(yōu)化SQL語句 in 和not in的替代方案
- mysql優(yōu)化配置參數
- SQL語句優(yōu)化之JOIN和LEFT JOIN 和 RIGHT JOIN語句的優(yōu)化
相關文章
遠程連接阿里云SqlServer 2012 數據庫服務器的圖文教程
在使用 阿里云 上的一些產品時,遇到不少坑,安裝IIS 時,遇到因買的配置過低,虛擬內存不足,而導致 IIS 總是安裝失敗,下面小編給大家分享遠程連接阿里云SqlServer 2012 數據庫服務器的圖文教程,一起看看吧2017-09-09SQL Server誤區(qū)30日談 第23天 有關鎖升級的誤區(qū)
在SQL Server 2005和之前的版本,頁鎖會直接升級到表鎖。在SQL Server 2005或SQL Server 2008,你可以通過如下跟蹤標志改變鎖升級的行為2013-01-01