[瘋狂Java]SQL-連線查詢:SQL92、SQL99
1. 連線查詢:
1) 即查詢的時候同時需要多張表(特別是存在外來鍵關係的),此時需要多張表之間的值進行連線;
2) 目前SQL標準提出過兩種連線查詢,第一種是較早的SQL92標準,第二種是目前使用廣泛的較新的SQL99標準;
3) 92形式簡單,但編寫較為冗長,99不僅在底層得到優化,而且形式看上去更加一目瞭然,邏輯性更強,一般建議使用99標準;
2. SQL92:
1) 多張表需要全部放在from之後,所有的連線條件都放在where當中,因此SQL92中的等值連線、非等值連結、外連線等等其實只是where中的條件不同罷了;
2) 具體語法:
-
select col1, col2, ...
-
from table1 t1, table2 t2, ...
-
where ...
!!可以看到需要為表取別名(可以使用as),t1、t2就是各個表的別名,這樣就可以在col1、col2、...以及where子句中使用引用這些別名了;
!!區別名的目的:一方面是為了簡化表名(原表名可能很長),其次可能發生自連線的情況(即自己跟自己連線),因此需要為同一張表取兩個不一樣的別名,這樣連線的時候可以“當做”兩張不同的表使用;
3) 示例:
-
select s.col1, t.col2
-
from table1 s, table2 t
-
1. where s.col1 = t.id; // 值相等就是等值連線
-
2. where s.col1 > t.id; // 不是相等就是非等值連線
-
3. ; // 無where子句就是廣義笛卡爾積
4) 連線的原理:按照from後面表的出現順序,前面的表作為記憶體的for迴圈,後出現的表作為外層的for迴圈,上面的例子就是這樣實現的
-
for t in table2
-
{
-
for s in talbe1
-
{
-
if 滿足where條件 // 廣義笛卡爾積就是去掉if語句直接output
-
output s + t;
-
}
-
}
!!可以看到where子句中以及select後的"表名(或者別名)+'.'+列名"的用法,在這裡表名(別名)其實就代表當前查出來的一條記錄了,而後面跟著的列名就代表該記錄的哪個欄位;
5) 自連線:就是一張表自己跟自己連線,比如員工和經理都在一張表中,現在要將員工的所屬經理的ID和經理的ID連線,此時就要將該表當成兩種表看,一張是普通的員工表,另一張是經理表(只不過經理標中載入這員工,員工表中夾雜著經理,通常需要自連線都表示資料庫設計不合理,非常不提倡);
-
select ...
-
from table s, table t
-
where ...
!!可以看到只要為同一張表命不同的別名加以區分就行了,可以帶到上面的巢狀for迴圈中檢驗一下;
6) 外連線:各資料庫對SQL92的外連線支援的並不是很好,基本都是在SQL99中全面支援外連線的,這裡MySQL就不支援SQL92的外連線;
!!以下內容全部都屬於SQL99:
3. SQL99、交叉連線(SQL92的廣義笛卡爾積):
1) 基本覆蓋了全部型別的連線查詢(包括外連線),並且各大資料庫都能全部支援SQL99的連線查詢;
2) 直接使用關鍵字:cross join(交叉連線,即廣義笛卡爾積)、natrual join(自然連線)、left join/right join(左右外連線)表示連線型別;
3) 使用"table xxx join table"的語法來表示哪張表和哪張表進行什麼樣的連線,連線型別一目瞭然,寫法更加符合邏輯,可讀性更好,並且底層支援地更好;
4) 交叉連線:即SQL92的廣義笛卡爾積,語法如下
-
select ...
-
from 表1
-
cross join 表2
-
[corss join 表3...]
!如果有很多表要交叉連線,那麼就繼續cross join下去;
5) 如果select選出的列剛好在各個表中有重名,則需要為表取別名以示區別;
6) 示例:
-
select s.col1, t.col2
-
from t1 as s
-
cross join t2 as t;
!!一般區別名還是建議使用as,如果一下用太多空格可讀性會大大降低!並且as見名知意;
4. 自然連線——通名列組合等值連線:
1) 語法跟交叉連線一模一樣,關鍵字是natural join,格式如下:
-
select ...
-
from 表1
-
natural join 表2
-
[natural join 表3...]
2) 自然連線看上去就是交叉連線,但不過自然連線是有條件,它是等值連線的一種,預設連線的兩張表同名列的組合相等,比如t1和t2進行自然連線,t1有a, b, c三列,t2有b, c, d三列,那麼自然連線時就是找組合t1.(b, c)=t2.(b, c)的記錄連線!記住是同列的組合!!
5. 指定同名列等值連線:
1) 前面的自然連線預設會將所有通名列的組合做等值連線,但SQL99也提供了join using語法來指定那些通名列可以做等值連線;
2) 如果多表之間有通名列,則可以人為指定用哪些通名列做等值連線而不是全部通名列的組合;
3) 格式:
-
select ...
-
from 表1
-
join 表2 using(指定通名列或組合)
-
join 表3 using(指定通名列或組合)
-
...
4) 例如:
-
select *
-
from t1
-
join t2 using(col1, col2);
!表示t1和t2以同名列col1、col2的組合做等值連線;
5) SQL99專門為通名列等值連線準備了兩個語法(natural join和join using),可以通名列等值連線在實際中還是非常常用的!
6. 自定義等值/不等值連線——對SQL92的擴充套件:
1) 上面介紹的等值連線都是基於通名列的,那如果想要作為連線條件的列不同名怎麼辦呢?
2) SQL99提供了join on來達到上述目的,其語法格式:
-
select ...
-
from 表1 別名1
-
join 表2 別名2 on 表1表2的連線條件
-
join 表3 別名3 on 表2表3的連線條件
-
...
3) 如果連線條件是相等判斷那就是等值連線,如果是大於、小於等之類的判斷那就是不等值連線了;
4) 例如:等值連線
-
select *
-
from t1
-
join t2 on t1.col1 = t2.col2;
5) 例如:不等值連線
-
select *
-
from t1
-
join t2 on t1.col1 > t2.col2;
6) 可以看到這和SQL92的語法非常相似,只不過92的條件是where子句確定的,而99的條件是由on確定的,但是92顯得更加簡單,為什麼要繼續開發出99呢?
7. 多表連線的邏輯——為什麼要用SQL99:
1) 假設3表連線,用92:
-
select *
-
from t1, t2, t3
-
where t1.col = t3.col and t2.col = t3.col;
!底層的for迴圈其實是由巢狀順序,而該SQL語句非常混亂,首先是巢狀順序各個資料庫實現可能不一樣,MySQL是t1(外)->t2(中)->t3(內),但其它資料庫的巢狀順序可能相反,但如果你明白的知道巢狀順序等細節就可以在寫SQL語句的時候對查詢進行優化;
2) 而SQL99不存在這個問題,它的join關鍵字其實就是一個顯示的for迴圈!!例如:
-
select *
-
from t1
-
join t2 on condition1
-
join t3 on condition2
!就是非常明確的:
-
for t1 in table1
-
{
-
for t2 in table2
-
{
-
if condition1
-
{
-
for t3 in table3
-
{
-
if condition2
-
output t1 + t2 + t3;
-
}
-
}
-
}
-
}
3) 因此在使用SQL99進行多表連線時,連線的順序本身就是底層for迴圈的順序,巢狀結構非常清晰,一目瞭然,就跟直接寫for巢狀一樣,因此99的多表連線顯而易見有如下幾個性質:
i. 連線的物件只能是兩張表,即兩兩連線,傳遞起來就看上去像多張表同時連線,其實就是最近的兩層for迴圈連線在了一起;
ii. 由for迴圈的作用於可知外層迴圈不能訪問記憶體迴圈的變數(內層對外層不可見),因此SQL99多表連線中上層表的condition中不能訪問下層表,比如在上面的例子中condition1裡不能出現t3,但是下層表可以訪問上層表(for迴圈巢狀中外層的變數對內層來說是可見的),即condition2中可以出現t1和t2!
8. 再看自然連線和using連線在超過2張表時的情形:
1) 先看自然連線:
-
select *
-
from t1
-
natural join t2
-
natural join t3
!的言下之意就是:
-
select *
-
from t1
-
join t2 using(t1-t2之間的同名列組)
-
join t3 using(t2-t3之間的同名列組)
!即條件是相鄰兩列之間的,而並非全部的!!
2) using同樣也符合這個道理,例如:
-
select *
-
from t1
-
join t2 using(col1)
-
join t3 using(col1)
!言下之意是:
-
select *
-
from t1
-
join t2 using(t1-t2之間的同名列col1)
-
join t3 using(t2-t3之間的同名列col1)
!!SQL92的多表連線查詢的連線條件永遠是相鄰兩列之間的!!!
9. 外連線:
1) 相比於外連線,之前的連線全部都算內連線,內連線就是隻要符合condition條件的連線都輸出,相反SQL的外連線不僅要把符合condition條件的連線輸出,也要把不符合condition的連線也輸出,但不是完整的輸出!!!
2) 什麼意思呢?先來看一下左外連線:
i. table1 左外連線 table2 condition 的言下之意是
-
for t1 in table1
-
{
-
for t2 in table2
-
{
-
if t1-t2滿足condition
-
output t1 + t2;
-
}
-
if table2中任意一個t2都不能讓<span style="font-family: Arial, Helvetica, sans-serif;">t1-t2滿足condition</span>
-
output t1 + null
-
}
!即滿足條件的連線還是正常地一條一條全部打印出來,但是如果左表(t1)的記錄找不到右表(t2)任意一條記錄能和它滿足條件,那麼就要輸出一條結果,就是不滿足條件的那個座標記錄,但右表的部分全部為null;
ii. 而右外連線正好相反(巢狀順序也相反):table1 右外連線 table2 condition 的言下之意是
-
for t2 in table2
-
{
-
for t1 in table1
-
{
-
if t1-t2滿足condition
-
output t1 + t2;
-
}
-
if table1中任意一個t1都不能讓t1-t2滿足condition
-
output null + t2;
-
}
iii. 全外連線就是左外和右外的結合:table1 全外連線 table2 condition 的言下之意是
-
for t1 in table1 // 滿足條件的全部輸出
-
{
-
for t2 in table2
-
{
-
if t1-t2滿足condition
-
output t1 + t2;
-
}
-
}
-
for t1 in table1 // 輸出左外連線
-
{
-
if table2中任意一個t2都不能讓t1-t2滿足condition
-
output t1 + null;
-
}
-
for t2 in table2 // 輸出右外連線
-
{
-
if table1中的任意一個t1都不能讓t1-t2滿足condition
-
output null + t2;
-
}
3) SQL99的外連線語法:
i. 關鍵字是left join(左外連線)、right join(右外連線)、full join(全外連線);
ii. 格式是:
-
select ...
-
from 表1
-
left/right/full join 表2 on 條件1
-
left/right/full join 表3 on 條件2
-
....