SQL裡面有樂子

2022-05-04 12:18:10 字數 4867 閱讀 6947

1、oracle的with應用場景;

2、connect by的簡單示例;

3、dual表與rownum的威力;

4、oracle中的隱式資料型別轉換;

5、sql效能的優化簡單策略;

6、關鍵有的例子挺有樂子哦;

sql語言主要操作增刪改,但是內部機理卻是非常複雜。在使用sql語言的發燒友裡,往往會有一句sql解決問題的要求。當然這樣sql往往會比較複雜,也會造成可讀性不好。但從sql語言這種「豐富」的表情裡面搜尋出一些非常有意義的玩法,還是非常有樂子的。下面我們就以傳說中的某面試題開始:

已知 小小+霸霸+王王=小霸王

小=?,霸=?,王=?

用sql求證

這道題正向思考時,就會想到需要三重迴圈,也就是說要有三表關聯來實現。只是這三張表可以自關聯。公司部門裡進行了一次有「可樂」獎品的測試,結果出現的結果真的是各放異彩。當然,最通用的方法是(下文的sql都是為了緊湊而盡量集結在一起的):

create table n_list (n int); --n為0-9

select a.n, b.n, c.n from n_list a, n_list b, n_list c

where 11 * (a.n + b.n + c.n) = a.n * 100 + b.n * 10 + c.n;

這樣得到結果是1,9,8和0,0,0。實際上n為0時意義不大可以忽略,使用oracle的一般寫法,也可以利用dual表和connect by來實現,即n_list表可以寫成類似結果:

select rownum-1 n from dual connect by rownum<11;

只要把它套入到sql中來替代n_list即可,但這樣sql還比較長。oracle從9i開始,還有可以利用with的寫法,那麼最終最簡潔而不借助其它資料庫表的寫法應該是:

with n_list as

( select rownum-1 n from dual connect by rownum<11 )

select a.n, b.n, c.n

from n_list a, n_list b, n_list c

where 11 * (a.n + b.n + c.n) = a.n * 100 + b.n * 10 + c.n;

由於上述演算法是從左到右,使用三表才實現,不用說開銷實際上不是最小。如果反向思考這個問題只要乙個迴圈就能解決。即把每乙個整數進行分拆成個十百位,看它的計算結果是否符合要求即可,它的語句實現是這樣的:

with n_list as ( select rownum n from dual connect by rownum<1000 )

select n from n_list where (substr(n, 1, 1) + substr(n, 2, 1) + substr(n, 3, 1)) * 11 = n and n > 100;

其實這裡用到oracle的隱性資料轉換,n是數值,通過substr變成字元,再通過計算時又變回數字。由於oracle不對資料型別進行強檢查,所以常會發生某個字串列進行計算時發生錯誤的情況,這也是使用oracle寫sql時要注意的問題。我們可以對比兩種寫法執行計畫之間差異:

單錶與三表之間的差距巨大。現在再來乙個類似的趣題吧:

每個字母代表數字0-9之間的數字,不同字母代表不同的數字

這請問a是幾?

這個題和上面的題有相類似之處,具體不需研究,只要給出sql來解決問題即可,很顯然,a的值肯定不是0,也不是1,當然在求證時不用細考慮。可以採用這樣的sql來完成:

with n_list as ( select a.n na, b.n ns, (a.n * 10 + b.n) * a.n man

from (select rownum n from dual connect by rownum<10) a,

(select rownum n from dual connect by rownum<10) b where a.n <> b.n )

select na, ns, man  from n_list where substr(man, 2, 1) = na and na <> ns

and substr(man, 1, 1) not in ( na, ns, substr(man,3,1)) and substr(man, 3, 1) not in ( na, ns );

此sql的關鍵點在於各不相同會比較麻煩一些,即要na與ns,man中的第1位和第3位不相同,也要考慮到man第3位與其它值也不相同,在with n_list中我們已經排除了as中的s與a的相同部分。這裡同樣有資料型別隱式轉換,所以才會看起來比較簡潔。

這個要求可能有點無理頭,如下的資料:

1001 徐兢 1003

1002 徐可可

1003 程大山 

分別**職工工號(zhigonggh)、姓名(xingming)和圖章號(tuzhanghao),這個圖章號是醫院裡面代表醫生的唯一標識(不是所有職工都有),由於用類似於以下的語句:

select * from zhigongid where zhigonggh='@01' or tuzhanghao='@01';

作為標準方案,當輸入重要條件是@01=1003時就會有多重資料的返回,要求這樣的結果:

1、如果所輸入在圖章號中能唯一對應(它是不會重複的),則返回它所對應的職工;

2、如果在圖章號未匹配到,則返回zhigonggh所對應的職工。

我們希望它能夠「適可而止」,即能根據需要只返回一條資料,這在介面層本來可以分兩次完成,或在後台通過儲存過程,但一條sql是皆大歡喜的。其基本思路是:

1、需要用union;

2、需要對tuzhanghao條件的資料進行計數;

3、需要把結果集作為條件。

首先select count(*) from gy_zgxx where tuzhanghao ='@01'這個條件是肯定要的;那麼如何實現乙個條件有資料,另外乙個條件為空呢?這裡就容易讓我們聯想到decode,in和空串,也就是說當統計出來的sql是一條資料時,我們讓它返回空串,把空串放入到in中,自然就不起作用了,所以可以這樣寫結果集:

select decode(b.rn,1,'',a.zhigonggh )

from gy_zgxx a, ( select count(*) rn from gy_zgxx where tuzhanghao='1003' ) b

where a.zhigonggh='1003;

當有圖章號時,返回都是空串,否則就是相應的zhigonggh,把它和圖章號的條件合併後就是:

with gy_zgxx as( select '1001' as zhigonggh, '徐兢' as xingming, '1003' as tuzhanghao from dual

union all select '1002', '徐可可', '' from dual union all select '1003', '程大山', '' from dual )

select zhigonggh from gy_zgxx where zhigonggh in (

select zhigonggh from gy_zgxx  where tuzhanghao='@01'

union select decode(b.rn, 1, '', a.zhigonggh)

from gy_zgxx a, (select count(*) rn from gy_zgxx where tuzhanghao = '@01') b

where a.zhigonggh = '@01' );

使用時把不同的條件套入到@01。此sql解決了輸入唯一的圖章號時只返回符號要求的一條資料。

乙個排班的資料,因為歷史遺留的原因,在資料中保留了上午、下午、晚上的三列資料,但是實際使用時需要按照每乙個時間段一行資料結果。所以就需要把三個列轉換成三行資料,通常寫法是這樣的:

with n_paiban as (

select '1001' zhigongid, 12 shangwuxh, 20 xiawuxh, 5 wanshangxh from dual

union all select '1002', 10, 10, 6 from dual union all

select '1003', 20, 20, 5 from dual )

select zhigongid, '上午', shangwuxh from n_paiban union all

select zhigongid, '下午', xiawuxh from n_paiban union all

select zhigongid, '晚上', wanshangxh from n_paiban;

這樣就會帶來乙個問題每一次union all就是一次遍歷。當用了更多的union al時開銷就會更大。而排班表又可能會與多表關聯,最終導致查詢的效能非常低下。由於表關聯的迪卡爾特性和decode函式的分支作用,我們可以做一張幾個紀錄的偽表與它進行關聯,即可以寫成類似於這樣的語句:

select zhigongid, decode(rn, 1, '上午', 2, '下午', 3, '晚上') shijian,

decode(rn, 1, shangwuxh, 2, xiawuxh, 3, wanshangxh) xianhaoshu

from n_paiban a, (select rownum rn from dual connect by rownum<4 );

這樣表n_paiban進行過一次簡單關聯後,實際上只進行一次掃瞄即可完成。效能基本上提高三倍,自然和union all的個數有關,當越多時效能提高越多,它的可讀性可能沒有原來強,只要是理解它,其實也沒有什麼難度。所以達到同樣的目的,更好地解決問題的sql其實還是很有樂子的。

笑話裡面有年味兒

1 近一年吧,拼命攢加借房子首付,吃的可謂慘不忍睹,這不過年了麼,和老婆提議咱們吃點好的?老婆想了想說 咱們加個菜,再加個魚吧。瞬間感動,只見老婆回身冰箱裡取出一袋魚泉榨菜 2 過年了,老婆給我1000元壓歲錢。我按照中國的傳統習俗虛讓了一下,僅僅一下。老婆竟然收回去了,還說不要拉倒,明年再說 到手...

笑話裡面有年味兒

開心一刻笑話大王 1 近一年吧,拼命攢加借房子首付,吃的可謂慘不忍睹,這不過年了麼,和老婆提議咱們吃點好的?老婆想了想說 咱們加個菜,再加個魚吧。瞬間感動,只見老婆回身冰箱裡取出一袋魚泉榨菜 2 過年了,老婆給我1000元壓歲錢。我按照中國的傳統習俗虛讓了一下,僅僅一下。老婆竟然收回去了,還說不要拉...

裡面有麵和點 學麵點

學麵點,新東方人本著 團結 務實 開拓 奉獻 的企業精神,為中華大地培養了數十萬名品學兼優的烹飪人才,為我國餐飲業的發展 為國家推行 再就業 工程做出了巨大的貢獻。乾貨丨麵包的5大發酵法門及發酵關鍵知識,快來get!我們都知道,做麵包必須要發酵,而發酵是乙個複雜的過程。簡單的說,酵母分解麵粉中的澱粉...