一、問題描述
首先說一下,判斷表里是否有記錄,常見的寫法(偽代碼)
-- 定義變量a
a = (select count(*) from tb) if a > 0 ... else ...
這么寫是沒錯,看上去也好理解,就是統計一下tb表中的記錄數,然后判斷這個記錄數是否大於0。可能絕大多數人在實現這個需求的時候,都會這么寫。
接下來,想想這個語句在執行時,會做什么處理:
1、如果tb表沒索引,那么會有表掃描,如果表中的記錄數很大,這個操作會很慢。
2、如果tb表有索引,那么會有索引掃描操作,計算索引記錄有多少條。同樣的,如果記錄數很大,這個操作不會太快,但肯定比第1種情況要快的多。
那么,有沒有更好的辦法,來實現這個需求呢?
二、優化方案
這里先想想這個需求本身,就是要判斷tb表中是否有記錄。上面這句話的意思很明確,仔細想想,判斷結果是:要么有,要么沒有,而至於表中到底有多少條記錄,這種精確的信息,他是不關注的。所以,我們可以朝着這個方向去優化,我想到的辦法有2個:
1、exists
a = case when exists(select id from tb) then 1 else 0 end
2、返回1條記錄
-- 方式1
a = (select count(1) from tb where rownum=1) -- 方式2
a = (select count(1) from (select top 1 id from tb ) t)
3、兩種方法的對比
上面的2種改進的寫法,雖然具體的實現方式不一樣,一個是用 exists,一個是用 top 或 rownum 來限制返回1條記錄,但意思是相同的,就是只看表里是否有記錄,不關注記錄數。
就這么一個簡單的需求,實現起來雖然很簡單,但是,如果不好好想想,深入理解需求的本質,恐怕也不容易有個好的實現(這里的“實現”兩字,表示把需求轉化為代碼的過程),更何況其他更加復雜的需求。
三、判斷記錄是否存在: exists 和 top 1 要比 count 快
之前在程序中驗證記錄是否存在,總是用 select count(0) from table1 得到記錄數,然后在比對,思考有沒有性能更好的辦法。
思考 count 的工作原理后感覺是讀取所有數據后進行統計的,而使用 top 1 會不會只是取得第一個紀錄后就跳出查詢呢?
正好手頭上有朋友上次做實驗制作的一個300萬記錄的表,我就用來做了下實驗
select count(0) from table1 -- 執行時間 兩次分別是 3秒,1秒
select top 1 0 from table1 -- 兩次執行時間都為0秒
exists( select 0 from table1) -- 兩次都為0秒