動機
sql的效能議題好像不常被提起 十分niche 那就來整理看看
原則
優先序由上往下
- 用index過的所有column(也只有被index過的)去select
- 如果需要反覆存取的資料上index與放到temp table
- 壓低要做操作的資料量
真的要用select *嗎?
通常不會需要所有的column,選需要的出來就好
避免用distinct
如果可以就select更多column出來就好
用join去join,不要用where去做
如果用where去做就是cross join,會讓資料量變大 效率自然就下去了
能用where去過濾的就用where
having只用在aggregate function的過濾
從sql的執行順序來看,因為where會先把資料量壓小 所以可以的話把條件放在where
wildcard只用在pattern最後面,如果只比對開頭的字串的話
是合理啦
但如果是要最尾的話,只放wildcard在前面效能也話比較好嗎
用limit(或top,FETCH FIRST,看DBMS)限制query出的數目,尤其是只想看個大概的時候
limit在sql的執行順序不是最高的,但是sql engine會知道不用撈整個table 這樣可以把資料量往下壓
把outer join換成空值+inner join
原本outer join會在不存在的column留下null 可以把null換成某個空值,這樣就可以用inner join
像是下面表的NO CUSTOMER
CUSTOMER_ID | CUSTOMER_NAME |
---|---|
0 | NO CUSTOMER |
1 | John Doe |
2 | Mary Jane |
3 | Peter Pan |
4 | Joe Soap |
CUSTOMER_ID | SALES_PERSON |
---|---|
0 | Newbee Smith |
2 | Oldie Jones |
1 | Another Oldie |
0 | Greenhorn |
把在join或是where中需要運算的部分放到row中
就是先算好
Before
SELECT *
FROM sales a
JOIN budget b ON ((year(a.sale_date)* 100) + month(a.sale_date)) = b.budget_year_month
After
SELECT * FROM PRODUCTSFROM sales a
JOIN budget b ON a.sale_year_month = b.budget_year_month
avoid n+1
要select或是update等等 就直接用sql一次做完
不要分成一次一個,DBMS不是記憶體,沒那麼快
subquery不要放在select的column中
這樣的話每一次select出新的row都要subquery一次 如下
SELECT c.Name,
c.City,
(SELECT CompanyName FROM Company WHERE ID = c.CompanyID) AS CompanyName
FROM Customer c
用join取代
SELECT c.Name,
c.City,
co.CompanyName
FROM Customer c
LEFT JOIN Company co
ON c.CompanyID = co.CompanyID
用exists來取代aggregate function,確認資料是否存在
Before
IF (SELECT COUNT(1) FROM EMPLOYEES WHERE FIRSTNAME LIKE '%JOHN%') > 0
PRINT 'YES'
After
IF EXISTS(SELECT FIRSTNAME FROM EMPLOYEES WHERE FIRSTNAME LIKE '%JOHN%')
PRINT 'YES'
如果一行做不完,用temp table存
如下
SELECT * INTO #Temp FROM Customer WHERE RegionID = 5
SELECT r.RegionName, t.Name FROM Region r JOIN #Temp t ON t.RegionID = r.RegionID
或是
table變數(根據soruce5,這個效能比較好)
DECLARE @AAAA TABLE
( A1 INT IDENTITY(1,1) PRIMARY KEY,
A2 VARCHAR(128),
A3 VARCHAR(128)
)
-- temp變數是這個
-- CREATE TABLE #AAAA
-- ( A1 INT IDENTITY(1,1) PRIMARY KEY,
-- A2 VARCHAR(128),
-- A3 VARCHAR(128)
-- )
Index
Index哪些column
Index完之後就不要一直改那些column的值
不然Btree是建心酸的嗎
在where或是order by中用index過的column
CREATE INDEX tbl_idx ON tbl (a, date_column)
SELECT *
FROM tbl
WHERE a = 12
ORDER BY date_column DESC
LIMIT 1
只有a與date_column在where與order by中,所以DBMS可以直接撈index的結果
index從左到右
CREATE INDEX tbl_idx ON tbl (a, b)
SELECT *
FROM tbl
WHERE a = 38
AND b = 1
SELECT *
FROM tbl
WHERE b = 1
第二個select就吃不到index的好處因為第一個index是a
只用index過的column
CREATE INDEX tbl_idx ON tbl (a, date_column)
SELECT date_column, count(*)
FROM tbl
WHERE a = 38
AND b = 1
GROUP BY date_column
因為b所以DBMS沒辦法從index過的結果拉資料,要去看table
如何tuning
- Execution Plan
- Monitor Resource Usage
- SQL DMVs
- profiler
- SQL SYNTAX
EXPLAIN