在csdn上回貼時,我總是苦口婆心地勸告樓上樓下的朋友們多用聯接。可響應甚微。往往一個簡單的功能,也一定要寫成子查詢或游標,弄得非常復雜冗長。的確,這樣寫對于初學者來說,費力不費腦,思路比較好理解。所以往往得分的也是這些回貼。可事實上,如果你真正熟悉了sql的編程風格,你會明白,聯接查詢才是最直接、最清晰、最有力的方法,而更好的辦法就是無招勝有招,一條簡單查詢結束戰斗。下面我舉幾個例子來證明一下這個觀點。
例1-1、重復記錄的查詢和處理
總有一些朋友在網上問,一個表中,有重復的記錄,怎么辦?當然,一個設計風格良好的關系型數據庫,每個表都應該有主鍵、有唯一索引,所以壓根就不該有重復記錄。不過有時還是會出現不該出現的事,比如“七.七事變”,比如“9.11”……咳咳,其實我想說的是,有時會有人根本沒有數據庫的概念,他就不知道主鍵是什么,或者隨意建了一個自動標識的id列充數(其實這也沒什么,沒有人天生會設計數據庫,關鍵是愿不愿承認自己的不足并且改進)。更常見的是我們的數據可能來自一些電子表格或文本文件,導入到數據庫中時才發現問題。
這里,我們建立一個表,表示某商店的存貨。我有意沒有加入任何索引和約束,這樣,它會很容易地出問題(就像實驗室里的裸鼠)。
create table product(
id int, pname char(20),
price money, number int,
pdescription varchar(50))
現在,我們可以向其中插入一些數據:
idpnamepricenumberpdescription
1apple 123000
1apple 123000
2banana 16.997600
3olive 25.224500
4orange 15.995500
4coco nut 40.992000
5pineapple 302500
6olive 25.223000
這里有一些明顯的問題,前兩行完全一樣,這樣的重復數據一點意義都沒有,只會添亂。interbase還好點,在它的ibconsole中可以直接修改它們。可在sql server中,系統根本無法區分這兩行,當我們試圖對其中任一行修改時會收到一個錯誤信息。事實上,這也是一個關系型數據庫應有的反應。那我們應該怎么辦呢?
事實上,處理它的方法比找出錯誤數據還簡單,聯接查詢都用不到。用一條sql語句
select distinct * from product
就可以把重復數據壓縮掉,生成一個包括正常數據的數據集。結果如下:
idpnamepricenumberpdescription
1apple 123000
2banana 16.997600
3olive 25.224500
4orange 15.995500
4coco nut 40.992000
5pineapple 302500
6olive 25.223000
對于支持select …… into……from語句的數據庫來說,這樣一句
select distinct * into newtable from product
就可把數據導入到一個新表(newtable )中。或者可以用inert into …… select distinct * from ……把它導入到一個現有的表中。總之有了正確的數據集,再如何處理就好辦了。相信大家知道這個合并重復數據的關鍵字distinct后,再不會用游標來處理重復數據了吧。
這是第一步,有時可能我們不想一下把它們壓縮掉,而是想先看看到底誰出了問題。好的,用下面的語句可以找出重復的記錄,最右邊一列“row_count”表示這行數據在表中重復的次數:
select id, pname, price, number, pdescription, count(*) row_count
from product
group by id, pname, price, number, pdescription
having count(*) > 1
idpnamepricenumberpdescriptionrow_count
1apple123000null2
(所影響的行數為 1 行)
其實就是關鍵字group by …… having和統計函數count的一個簡單運用,記得在group by 后面寫上完整的字段列表。這表示我們要的是那些完全一致的數據,每個字段都一樣。
product表中的數據很多時,用前面的方法直接生成正確的數據集效率很低。現在有了這個結果集,我們可以高效率工作了。現在,我們用
select id, pname, price, number, pdescription
from product
group by id, pname, price, number, pdescription
having count(*) > 1
把重復的數據生成為一個經過壓縮的正確數據集,用前述的方法導出到一個臨時表中,然后用
delete from product
where id in (
select id
from product
group by id, pname, price, number, pdescription
having count(*) > 1
)
把重復數據從product表中刪除,再把壓縮好的數據插入product。現在product表中不再有完全重復,不可標識的數據了。