雙重NOT EXISTS經典分析

2021-06-26 12:46:32 字數 4142 閱讀 4988

create table  j(jno varchar(5) not null primary key,

jname varchar(20) not null,

leader varchar(10),

bg int)

create table  spj(sno varchar(5) not null,

pno varchar(5) not null,

jno varchar(5) not null,

qty int,

constraint pr_spj primary key(sno,pno,jno))

create table p(pno varchar(5) not null primary key,

pname varchar(20) not null,

spec varchar(20),

city nvarchar(20);

color nvarchar(2))

以上是建立的三個表

找出使用**商s2**的全部零件的工程號。

select jno

from j 

where not exists

(select *

from spj x

where x.sno=』s2』 and not exists

(select * 

from spj y

where y.jno=j.jno and y.pno=x.pno))

網上有一些關於exists 說明的例子,但都說的不是很詳細.比如對於著名的供貨商資料庫,查詢:找出**所有零件的**商的**商名,對於這個查詢,網上一些關於exists的說明文章都不能講清楚.

我先解釋本文所用的資料庫例子,''供貨商'' 資料庫,共3個表. 供貨商表 s(s#,sname), 貨物表 p(p#,pname), 供貨商-貨物表 sp(s#,p#). 欄位s#,p#分別代表供貨商和貨物的id.

在c.j.date的資料庫系統導論第八版中文版第147頁給出了, exists的比較正規的解釋, "exists( select ... from ...)取真值,當且僅當 select ... from ... 取非空值.在作為相關子查詢的例子中,sql涉及子查詢,因此它包含了一範圍變數的引用,即隱式範圍變數s, 它在外查詢中定義."

我個人認為,此處所指的外查詢定義的隱式範圍變數s, 可以用另外一種方法來解釋: 將外查詢表的每一行,代入內查詢作為檢驗, 如果內查詢返回的結果取非空值,則exists子句返回true, 這一行行可作為外查詢的結果行, 否則不能作為結果.

至此可以明確,exists(包括 not exists )子句的返回值是乙個bool值. exists內部有乙個子查詢語句(select ... from...), 我將其稱為exist的內查詢語句.其內查詢語句返回乙個結果集. exists子句根據其內查詢語句的結果集空或者非空,返回乙個布林值.

舉一例子說明: 找出**所有零件的**商的**商名

select distinct s.sname

from s

where not exists

(select * 

from p

where not exists

(select *

from sp

where sp.s#=s.s#

and sp.p#=p.p#) );

假設資料如下:

s s# sname 

1    s1 

2     s2

p p# pname 

1     p1 

2     p2

sp s# p# 

1     1 

1     2 

2     1

這個查詢過程如下:

step1:      將s表第一行(1,s1) 作為隱式變數v1, 代入第乙個not exists子句. 由於這個子句巢狀乙個not exists子句, 再將 p表第一行(1,p1) 作為隱式變數v2, 和v1一起代入第二個not exists子句中, 這時第二個not exists的內查詢子句變成

select *

from sp

where sp.s#=1

and sp.p#=1

其返回結果集為

s#          p# 

1             1

這個返回結果集非空,注意not exists子句返回的是exists子句的非,因此 第二個not exists 子句返回false. 因此v2不能加入第乙個not exists子句的內查詢子句返回結果.

同理,將p表第二行(2,p2)作為隱式變數v3, 與v1一起代入第二個not exists子句中,內查詢返回結果集非空(返回 行(1,2) ), 因此v3也不能加入第乙個not exists子句的內查詢返回結果集.

至此, 對於隱式變數v1(也就是s的第一行), p表的每一行都已代入第二個not exists子句中進行檢驗,返回結果是乙個空集, 因此對於第乙個not exists子句,其內查詢子句返回結果為空.因此,第乙個not exists子句返回true.因此, v1(1,s1)加入外查詢的結果集.

step 2:    將s表的第二行(2,s2)作為隱式變數 v4, 代入第乙個 not exists 子句. 將 v4,v2, 一起代入第二個not exists子句. 第二個not exists子句內查詢結果集返回非空(2,1),第二個not exists子句返回false.v2 不能加入第乙個not exists子句的內查詢結果集.

將v4,v3 一起代入第二個not exists子句, 這時第二個not exists子句的內查詢子句變成:

select *

from sp

where sp.s#=2

and sp.p#=2

在sp表中,並沒有s#=2 and p#=2 的一行,因此,第二個not exists子句的內查詢子句返回空集,第二個not exists子句返回 true. 因此v3, 可以插入第乙個not exists子查詢結果集.

至此, 對於隱式變數v4(也就是s的第2行), p表的每一行都已代入第二個not exists子句中進行檢驗.第乙個not exists子查詢語句返回結果集為:

p# pname 

2      p2

非空,因此第乙個not exists子句返回false,v4(2,s2) 不能加入外查詢的結果集.

至此s表的每一行都代入第乙個not exists子句中進行檢驗, 外查詢的返回結果是

sname 

s1查詢結束.

從上述查詢過程來可以得知, 第二個not exists子句的內查詢語句返回的結果集的含義是, 乙個供貨商能否**某種貨物. 第乙個not exists的內查詢語句返回的結果集的含義是, 某乙個供貨商不能**所有的貨物. 而連起來使用,就是用排除法得到"沒有不能**的貨物的某一供貨商", 也就是能**所有貨物的供貨商.

深度分析:從上面的分析可知,第乙個not extsts的作用是逐行掃瞄**商表s的**商與貨物表p的所有商品(元組)進行匹配,這樣匹配的結果檢查出「沒有不能**所有貨物的某一**商」。那麼我們能否這樣設想:直接用2個exists找出提供了所有貨物的**商。也就是將語句改為:

select distinct s.sname

from s

where exists

(select * 

from p

where exists

(select *

from sp

where sp.s#=s.s#

and sp.p#=p.p#) );

這樣改的結果:會將s1,s2這2個**商都選擇出來。為什麼?在這裡,需要解釋一下exists和not exist對於查詢結果的不同處理。同樣對於s的第一行,第二個exists的子查詢返回結果(1,1),(1,2),可以加入第乙個exists的子查詢結果,同樣可以加入外查詢結果,外查詢返回s1;關鍵是對於s的第二行,第二個exists的子查詢返回(2,1),null,這樣的結果能否加入第乙個exists的子查詢結果?答案是肯定的,也就是在exists的子查詢結果中是可以包含是空行的,只要其中有非空行就可以加入父查詢結果;但是not exists是不可以包含空行的,只要其中有空行就不能加入父查詢結果。實際上,從語義上也很好理解:「存在」只要有就表明存在;「不存在」全部都沒有才能表明不存在。所以這個雙重exists查詢的結果實際上是:找出所有提供了零件的**商的**名。

資料庫not exists 例題分析

查詢選修了所有課程的學生姓名 sc表 sno是學生號,cno是課程號 sno cno grade 201215121 1 92 201215121 2 85 201215121 3 88 201215122 2 90 201215122 3 80 student表 sno sname s sage ...

鍊錶InitList傳入雙重指標分析

typedef int element typedef int status typedef struct node node,linklist status initlink linklist l 答 因為鍊錶只有定義成雙重指標,這裡才能把頭結點給帶出來。如果這裡用單指標 linklist l 的...

記憶體經典案例分析

練習 package com.heima.collection public class example public static void main string args public void change string str,char ch 記憶體執行過程 過程解析 從main方法中開始...