1. 程式人生 > >資料庫開發技術 課堂筆記6 查詢優化器和SQL優化

資料庫開發技術 課堂筆記6 查詢優化器和SQL優化

本文主要來源課堂筆記和PPT

為什麼會有SQL優化?

因為SQL基於關係代數,可以進行等價變換

SQL的優化與所寫的SQL語句有關,有時為了優化SQL,需要重寫SQL。

在某查詢中,條件A可以保留10%的結果,條件B可以保留80%的結果,出於減少查詢的中間狀態數的目的,此時我們稱A條件是好條件。

假設有100條記錄。

先A後B: 100108

先B後A: 100808

好條件要先做查詢

避免在最高層使用distinct。

例如,在選擇人名的時候,會出現同名的現象。

劉嘉故事會時間:

比如選出南京最近六個月買寶馬的人,有買7輛不同顏色配衣服的劉嘉,也有買打折寶馬T恤的劉嘉。

下面的錯誤示例都是說的distinct

錯誤的SQL語句1:

select distinct c.custname
from customers c
join orders o
on o.custid = c.custid
join orderdetail od
on od.ordid = o.ordid
join articles a
on a.artid = od.artid
where c.city = ‘Nanjing'
and a.artname = ‘BMW'
and o.ordered >= somefunc /*函式,返回六個月前的具體日期*/

錯誤的SQL語句2:

select distinct c.custname
from customers c,
orders o,
orderdetail od,
articles a
where c.city = ‘Nanjing'
and c.custid = o.custid
and o.ordid = od.ordid
and od.artid = a.artid
and a.artname = ‘BMW'
and o.ordered >= somefunc

擺脫distinct的方法:

顯然是需要引入一個新的量來描述不同的顧客,常用的還是custid

使用巢狀子查詢的方法:

select
c.custname from customers c where c.city = ‘Nanjing' and exists (select null from orders o, orderdetail od, articles a where a.artname = ‘BMW' and a.artid = od.artid and od.ordid = o.ordid and o.custid = c.custid and o.ordered >= somefunc )

使用非關聯子查詢的方法:

select custname
from customers
where city = ‘Nanjing'
and custid in (select o.custid
from orders o,
orderdetail od,
articles a
where a.artname = ‘BMW'
and a.artid = od.artid
and od.ordid = o.ordid
and o.ordered >= somefunc)

如果使用巢狀子查詢,那麼就需要外層的select條件比較好

通過SQL改寫來指引查詢優化器工作

  1. 找到解析度最強的條件
  2. 解決方案不止一種,查詢和資料隱含的假設密切相關
  3. 預先考慮優化器的工作,以確定它能找到所需要的資料

大資料量查詢

  • 儘快剔除不需要的資料,即使用好條件

將子查詢轉換為JOIN

  • 不包含聚合函式,不出現多種條件選擇則不需要子查詢

例:

Jobs(employee,title) Ranks(title,rank)Salary(rank,payment)

使用子查詢的方法:

Select payment from salary where rank=
(select rank from ranks where title=
(select title from jobs where employee = ‘…’))

使用JOIN的方法:

Select payment from salary, ranks,jobs
Where salary.rank = ranks.rank
And ranks.title = jobs.title
And jobs.employee = ‘…’

查詢不存在的內容

例:

是否存在某個等級當前沒有分配職位

暴力方法:

Select salary.ranks from salary
Where rank NOT IN (select rank from ranks)

外連線:

Select salary.rank
From salary
LEFT OUTER JOIN ON(salary.rank = ranks.rank)
Where ranks.rank IS NULL

將聚合子查詢轉換為JOIN

例:

Orders(custid,ordered,totalitems)

需要顯示每一個客戶購物件數最多的日期

聚合子查詢:

Select custid, ordered, totalitems
From orders o1
Where o1.ordered = (
select max(ordered)
from orders o2
where o1.custid = o2.custid )

JOIN查詢:

Select o1.custid, o1.ordered, o1.totalitems
From orders o1
JOIN orders o2 on (o1.custid = o2.custid)
Group by o1.custid, o1.ordered, o1.totalitems
Having o1.ordered = max(o2.ordered)

非關聯子查詢變成內嵌檢視

例:

在訂單完成前有不同狀態,記錄在orderstatus(ordid,status,statusdate)中

需求是:列出所有尚未標記為完成狀態的訂單的下列欄位:訂單號,客戶名,訂單的最後狀態,以及設定狀態的時間

原始寫法:

select c.custname, o.ordid, os.status, os.statusdate
from customers c,
orders o,
orderstatus os
where o.ordid = os.ordid
and not exists (select null
from orderstatus os2
where os2.status = 'COMPLETE'
and os2.ordid = o.ordid)
and os.statusdate = (select max(statusdate)
from orderstatus os3
where os3.ordid = o.ordid)
and o.custid = c.custid

修改後寫法:

select c.custname, o.ordid, os.status, os.statusdate
from customers c,
orders o,
orderstatus os,
(select ordid, max(statusdate) laststatusdate
from orderstatus
group by ordid) x
where o.ordid = os.ordid
and os.statusdate = x.laststatusdate
and os.ordid = x.ordid
and os.status != 'COMPLETE'
and o.custid = c.custid