查询在一张表不在另外一张表的记录
題目
假如要查詢?cè)赼表中存在,但是在b表中不存在的記錄,應(yīng)該如何查詢。為了便于說(shuō)明,我們假設(shè)a表和b表都只有一個(gè)字段id,a表中的記錄為{1,2,3,4,5},b表中的記錄為{2,4},那么我們需要通過(guò)一個(gè)sql查詢得到{1,3,5}這樣的結(jié)果集。
一般解法(效率低)
看到這個(gè)題目,我們首先想到的可能就是not in這樣的關(guān)鍵字,具體的查詢語(yǔ)句如下:
select ta.* from ta where ta.id not in(select tb.id from tb)上述查詢語(yǔ)句的查詢結(jié)果集確實(shí)是{1,3,5},用navicat執(zhí)行上述語(yǔ)句,得到如下圖所示結(jié)果:
效率分析
但是仔細(xì)分析我們可以發(fā)現(xiàn),如果b表很長(zhǎng),那么執(zhí)行上述的查詢語(yǔ)句,需要用a表中的字段去匹配b表中的每一個(gè)字段,相當(dāng)于是a表的每一個(gè)字段都要遍歷一次b表,效率非常低下。(只要a中的字段不在b表中那么肯定要遍歷完b表,如果a表中的字段在b表中,那么只要遍歷到就退出,進(jìn)行a表中下一個(gè)字段的匹配)
使用連接解決
連接查詢使我們平時(shí)進(jìn)行sql查詢用到最多的操作之一了,相對(duì)于上述not in關(guān)鍵字,我們使用連接查詢的效率更高。因?yàn)槲覀冃枰阉鞯氖莂表中的內(nèi)容,所以使用a表左連接b表,這樣b表中會(huì)補(bǔ)null,查詢語(yǔ)句如下:
select * from ta left join tb on ta.id=tb.id上述查詢語(yǔ)句的查詢結(jié)果如下:
因?yàn)閍、b兩表中字段id相同,所以上述b表中的id字段變成了id1。仔細(xì)觀察由可以發(fā)現(xiàn),我們需要的結(jié)果集{1,3,5}所對(duì)應(yīng)的id1字段都是null。這樣我們?cè)谏鲜龅牟樵冋Z(yǔ)句中加入條件即可完成對(duì)只在a表中,但不在b表中的結(jié)果集的插敘,查詢語(yǔ)句如下:
select * from ta left join tb on ta.id=tb.id where tb.id is null查詢結(jié)果如下圖所示:
但是我們又發(fā)現(xiàn)上述查詢結(jié)果有2列,也就是a表和b表的連接查詢結(jié)果,但是我們只需要a表中的內(nèi)容,所以對(duì)上述查詢稍作修改:
select ta.* from ta left join tb on ta.id=tb.id where tb.id is null查詢結(jié)果如下圖所示:
以上就是我們所要求的查詢結(jié)果。
詳解(PS:2012-9-7)
數(shù)據(jù)準(zhǔn)備
use TESTDB3 --1.創(chuàng)建表,堆結(jié)構(gòu),ta,大表 CREATE TABLE ta (id INT );--2.創(chuàng)建表,堆結(jié)構(gòu),tb,小表 CREATE TABLE tb (id INT );--3.插入10000條記錄到ta SET NOCOUNT ON; GO DECLARE @i int; SET @i = 1; WHILE @i <= 10000 BEGININSERT INTO taSELECT @i;SET @i = @i + 1;END; GO--4.往tb中插入少數(shù)數(shù)據(jù) insert into tb values(1); insert into tb values(111); insert into tb values(11); insert into tb values(11111111); insert into tb values(1222222);時(shí)隔三個(gè)月再來(lái)看這道題目,又有新的發(fā)現(xiàn),之前還是只是半知半解,在寫(xiě)完SQL Server Join方式這篇博客以后基本就明白這道題目的核心了,核心是:我應(yīng)該使用何種聯(lián)接方式來(lái)查詢結(jié)果。
我們能夠?qū)懗鰜?lái)的最直觀的TSQL語(yǔ)句應(yīng)該是:
select ta.* from ta where ta.id not in(select tb.id from tb)然后我們看看這個(gè)語(yǔ)句的查詢計(jì)劃:
從上圖我們可以發(fā)現(xiàn)使用了Nested Loops的聯(lián)接方式,但是我們知道nested loop聯(lián)接方式的使用場(chǎng)景是:比較適合于兩個(gè)比較小的結(jié)果集做聯(lián)接,或者至少是Outer table的結(jié)果集比較小。而上面的outer table是ta,它是大表,所以可以發(fā)現(xiàn)nested loop不適合。注意:雖然上面的查詢語(yǔ)句中沒(méi)有join字段,但是還是使用了join。
假如我們使用left join 來(lái)寫(xiě)查詢語(yǔ)句的話,sql server會(huì)幫我們選擇何種聯(lián)接方式呢?測(cè)試如下:
select ta.id from ta left join tb on ta.id=tb.id where tb.id is NULL--Hash Match上述查詢的執(zhí)行執(zhí)行計(jì)劃如下圖所示:
從上圖我們可以發(fā)現(xiàn)sql server幫我們選擇了使用Hash Match。這是因?yàn)樵谏鲜雎?lián)接中,ta是大表,ta和tb兩表之間數(shù)據(jù)量差距很大,還有ta和tb都沒(méi)有索引。從執(zhí)行計(jì)劃的TotalSubtreeCost中也可以看出來(lái),使用Hash Match的TotalSubtreeCost=0.12,而是用Nested Loop的TotalSubtreeCost=1.03。可以發(fā)現(xiàn)Hash Match性能比Nest Loop好很多。
那么使用Merge Join能,起性能如何?我們可以通過(guò)使用sql hint來(lái)建議sql server使用特定的聯(lián)接方式,執(zhí)行如下TSQL語(yǔ)句:
select ta.id from ta left merge join tb on ta.id=tb.id where tb.id is NULL--Merge Join其執(zhí)行計(jì)劃如下圖所示:
從上圖可以看出:
所以我們?cè)诨卮鹕厦骖}目的時(shí)候,必須說(shuō)明使用Hash Match,而不只是給出left join的答案,之所以查詢結(jié)果最有是因?yàn)閟ql server幫我們分析了使用Hash Match性能最優(yōu)。
所有查詢方式:
select ta.* from ta where ta.id not in(select tb.id from tb) select ta.id from ta where ta.id not in(select tb.id from tb)--Nested Loops select ta.id from ta left loop join tb on ta.id=tb.id where tb.id is NULL--Nested Loops select ta.id from ta left merge join tb on ta.id=tb.id where tb.id is NULL--Merge Join select ta.id from ta left hash join tb on ta.id=tb.id where tb.id is NULL--Hash Match?
總結(jié)
以上是生活随笔為你收集整理的查询在一张表不在另外一张表的记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Hadoop Streaming编程实例
- 下一篇: 找出数组中两个只出现一次的数字