SQL 聯接分類和本質

2021-09-02 09:33:17 字數 3481 閱讀 1752

目錄

1、巢狀迴圈聯接

2、合併聯接

3、雜湊聯接

聯接(join)是將兩個表合併為乙個表的操作。sol的聯接分為外聯接、內聯接和交叉聯接。本文將主要通過查詢執行計畫的角度從本質上講解聯接。

巢狀迴圈聯接(nested loop join)也稱為「巢狀選代」,它將乙個聯接輸入用作外部輸入表(顯示為圖形執行計畫中的頂端輸入),將另乙個聯接輸入用作內部(底端)輸入表。外部迴圈逐行處理外部輸入表。內部迴圈會針對每個外部行執行,在內部輸入表中搜尋匹配行。

最簡單的情況是,搜尋時掃瞄整個表或索引,這稱為「單純巢狀迴圈聯接」。如果搜尋時使用索引,則稱為「索引巢狀迴圈聯接」。如果將索引生成為查詢計畫的一部分(並在查詢完成後立即將索引破壞),則稱為「臨時索引套迴圈聯接」。查詢優化器考慮了所有這些不同的情況。

如果外部輸入較小而內部輸入較大,且預先建立了案引,則使用巢狀迴圈聯接尤其有效,在許多小事務中(如那些只影響較小的一組行的事務),索引巢狀迴圈聯接優於合併聯接和雜湊聯接。但在大型查詢中,巢狀迴圈聯接通常不是最佳選擇。

在程式中可以理解為巢狀的for迴圈語句,先是對外部輸入表迴圈找到每行資料,再使用迴圈對內部輸入表的每行資料與外部輸入表的資料進行匹配,直到這2個迴圈都完成。例如建立班級表和學生表,然後對這兩個表進行內聯接,則系統將採用巢狀迴圈聯接對這兩個表進行處理。如下:

if object_id('class') is not null

drop table class;

gocreate table class

(    cid int not null primary key,

cname varchar(10) not null

);go

if object_id('student') is not null

drop table student;

gocreate table student

(    sid int identity primary key,

cid int not null,

sname varchar(10) not null,

constraint fk_student_class foreign key(cid) references class(cid)

);go

--插入測試資料

insert into class values

(1,'01'),

(2,'02');

insert into student(cid,sname) values

(1,'s11'),

(1,'s12'),

(2,'s21'),

(2,'s22'),

(2,'s23');

內聯接查詢這2個表,同時返回查詢的執行計畫,查詢**如下:

--內聯接

select *

from student as s

inner join class as c

on s.cid = c.cid;

執行計畫:

合併聯接(merge join)要求兩個輸入都在合併列上排序,其由聯接謂詞的等效(on)子句定義。通常,利用查詢優化器掃瞄索引(如果在適當的一組列上存在索引),或在合併聯接的下面放乙個排序運算子。在極少數情況下,雖然可能有多個等效子句,但只用其中一些可用的等效子句獲得合併列。

由於每個輸入都已排序,因此 merge join運算子將從每個輸入獲取一行並將其進行比較。例如,對於內聯接操作( inner.jon),如果獲取的行相等則返回該行。如果行不相等,則拋棄值較小的行並從該輸入獲得另一行進行比較。這一過程將重複進行,直到處理完所有的行為止。

合併聯接操作可以是常規操作,也可以是多對多操作。多對多合併聯接使用臨時表儲存行。如果每個輸入中有重複值,則在處理其中乙個輸入中的每個重複項時,另乙個輸入必須重繞到重複項的開始位置。

如果存在駐留謂詞,則所有滿足合併謂詞的行都將對該駐留謂詞取值,而只返回那些滿足該駐留謂詞的行。

合併聯接本身的速度很快,但如果需要執行排序操作,選擇合併聯接就會非常費時。然而,如果資料量很大且能夠從現有b樹索引中獲得預排序的所需資料,則合併聯接通常是最快的可用聯接演算法。例如仍然使用前面建立的班級和學生表,由於合併聯接在兩表資料量並不小而且聯接列已排序的情況下發生,所以需要向表中新增大量資料,同時還要為進行聯接的列cid排序,具體sql**如下:

set nocount on

--建立大量的測試資料

declare @i int =3

while @i <1000

begin

insert into class values(@i,'01')

declare @j int  =0

while @j<10

begin

insert into student(cid,sname) values

(@i,'s'+convert(varchar(5),@i))

set @j += 1

endset @i += 1

endgo

--建立索引

create index ix_student_cid

on student(cid) include(sid,sname);

接下來執行兩個表的聯接查詢(上面的查詢語句),可以看到執行計畫中使用了合併聯接:

雜湊聯接(hash join)有兩種輸入,即生成輸入和探測輸入。如果兩個聯接輸入都大,而且大小差不多,則預先排序的合併聯接提供的效能與雜湊聯接相近。但是,如果這兩個輸入的大小相差很大,則雜湊聯接操作通常快得多。

雜湊聯接先掃瞄或計算整個生成輸入,然後在記憶體中生成雜湊表。根據計算得出的希鍵的雜湊值,將每行插入雜湊儲存桶。如果整個生成輸入小於可用記憶體,則可以將所有行都插入雜湊表中。生成階段之後是探測階段。一次一行地對整個探測輸入進行掃瞄或計算,並為每個探測行計算雜湊鍵的值,掃瞄相應的雜湊儲存桶並生成匹配項。

雜湊聯接一般在一張小表和一張大表進行聯接時應用。例如對於班級表和學生表,明顯班級表要比學生錶小很多,在去掉學生表上對cid的排序後,這兩個表在進行聯接運算時將使用雜湊聯接。詳細**如下:

--刪除索引

drop index ix_student_cid on student;

go--執行查詢

select *

from student as s

inner join class as c

on s.cid = c.cid

執行計畫如下:

SQL聯接查詢

舉例有兩表資訊如下 以上兩種查詢方式等價,如下圖所示內聯接inner join只取兩表存在關聯關係的資料 注 inner join與join相同,inner可省略不寫。查詢結果如下 如下圖所示,左聯接 左外聯接 左表 user表 資料將會完全展示,右表 dept表 只展示與左表存在關聯關係的資料。注...

SQL聯接查詢

聯接查詢 join表操作符對兩個輸入表進行了操作。聯結有三種基本型別 交叉連線,內連線,外鏈結。這三種連線的區別是它們採用的邏輯查詢處理步驟各部相同,每種連線都有一套不同的步驟。交叉連線只有乙個步驟 笛卡爾積 內連線有兩個步驟 笛卡爾積,過濾 外鏈結有三個步驟 笛卡爾積,過濾,新增外部行。交叉連線 ...

sql 左聯接,右聯接,內聯接的比較

首先需要解釋一下這幾個聯接的意思 2 left join 左聯接 返回包括左表中的所有記錄和右表中聯結字段相等的記錄。3 right join 右聯接 返回包括右表中的所有記錄和左表中聯結字段相等的記錄。inner join 等值連線 只返回兩個表中聯結字段相等的行。接下來,建立乙個資料庫,然後建立...