1. 程式人生 > 資料庫 >sql--查詢(多表,連線)

sql--查詢(多表,連線)

多表查詢

SELECT查詢不但可以從一張表查詢資料,還可以從多張表同時查詢資料。查詢多張表的語法是:SELECT * FROM <表1> <表2>。

例如,同時從students表和classes表的“乘積”,即查詢資料,可以這麼寫:

-- FROM students, classes:

SELECT * FROM students, classes;

這種一次查詢兩個表的資料,查詢的結果也是一個二維表,它是students表和classes表的“乘積”,即students表的每一行與classes表的每一行都兩兩拼在一起返回。結果集的列數是students表和classes表的列數之和,行數是students表和classes表的行數之積。

這種多表查詢又稱笛卡爾查詢,使用笛卡爾查詢時要非常小心,由於結果集是目標表的行數乘積,對兩個各自有100行記錄的表進行笛卡爾查詢將返回1萬條記錄,對兩個各自有1萬行記錄的表進行笛卡爾查詢將返回1億條記錄。

你可能還注意到了,上述查詢的結果集有兩列id和兩列name,兩列id是因為其中一列是students表的id,而另一列是classes表的id,但是在結果集中,不好區分。兩列name同理

要解決這個問題,我們仍然可以利用投影查詢的“設定列的別名”來給兩個表各自的id和name列起別名:

-- set alias:

SELECT
students.id sid,
students.name,
students.gender,

students.score,
classes.id cid,
classes.name cname
FROM students, classes;

注意,多表查詢時,要使用表名.列名這樣的方式來引用列和設定別名,這樣就避免了結果集的列名重複問題。但是,用表名.列名這種方式列舉兩個表的所有列實在是很麻煩,所以SQL還允許給表設定一個別名,讓我們在投影查詢中引用起來稍微簡潔一點:

-- set table alias:

SELECT
s.id sid,
s.name,
s.gender,
s.score,
c.id cid,
c.name cname
FROM students s, classes c;

注意到FROM子句給表設定別名的語法是FROM <表名1> <別名1>, <表名2> <別名2>。這樣我們用別名s和c分別表示students表和classes表。

多表查詢也是可以新增WHERE條件的,我們來試試:

-- set where clause:

SELECT
s.id sid,
s.name,
s.gender,
s.score,
c.id cid,
c.name cname
FROM students s, classes c
WHERE s.gender = 'M' AND c.id = 1;

這個查詢的結果集每行記錄都滿足條件s.gender = 'M'和c.id = 1。新增WHERE條件後結果集的數量大大減少了。

小結

  • 使用多表查詢可以獲取M x N行記錄;
  • 多表查詢的結果集可能非常巨大,要小心使用。

連線查詢

連線查詢是另一種型別的多表查詢。連線查詢對多個表進行JOIN運算,簡單地說,就是先確定一個主表作為結果集,然後,把其他表的行有選擇性地“連線”在主表結果集上。

例如,我們想要選出students表的所有學生資訊,可以用一條簡單的SELECT語句完成:

-- 選出所有學生

SELECT s.id, s.name, s.class_id, s.gender, s.score FROM students s;

但是,假設我們希望結果集同時包含所在班級的名稱,上面的結果集只有class_id列,缺少對應班級的name列。

現在問題來了,存放班級名稱的name列儲存在classes表中,只有根據students表的class_id,找到classes表對應的行,再取出name列,就可以獲得班級名稱。

這時,連線查詢就派上了用場。我們先使用最常用的一種內連線——INNER JOIN來實現:

-- 選出所有學生,同時返回班級名稱

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
INNER JOIN classes c
ON s.class_id = c.id;

注意INNER JOIN查詢的寫法是:

先確定主表,仍然使用FROM <表1>的語法;
再確定需要連線的表,使用INNER JOIN <表2>的語法;
然後確定連線條件,使用ON <條件...>,這裡的條件是s.class_id = c.id,表示students表的class_id列與classes表的id列相同的行需要連線;
可選:加上WHERE子句、ORDER BY等子句。
使用別名不是必須的,但可以更好地簡化查詢語句。

那什麼是內連線(INNER JOIN)呢?先彆著急,有內連線(INNER JOIN)就有外連線(OUTER JOIN)。我們把內連線查詢改成外連線查詢,看看效果:

-- 使用OUTER JOIN

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
RIGHT OUTER JOIN classes c
ON s.class_id = c.id;

執行上述RIGHT OUTER JOIN可以看到,和INNER JOIN相比,RIGHT OUTER JOIN多了一行,多出來的一行是“四班”,但是,學生相關的列如name、gender、score都為NULL。

這也容易理解,因為根據ON條件s.class_id = c.id,classes表的id=4的行正是“四班”,但是,students表中並不存在class_id=4的行。

有RIGHT OUTER JOIN,就有LEFT OUTER JOIN,以及FULL OUTER JOIN。它們的區別是:

INNER JOIN只返回同時存在於兩張表的行資料,由於students表的class_id包含1,2,3,classes表的id包含1,2,3,4,所以,INNER JOIN根據條件s.class_id = c.id返回的結果集僅包含1,2,3。

RIGHT OUTER JOIN返回右表都存在的行。如果某一行僅在右表存在,那麼結果集就會以NULL填充剩下的欄位。

LEFT OUTER JOIN則返回左表都存在的行。如果我們給students表增加一行,並新增class_id=5,由於classes表並不存在id=5的行,所以,LEFT OUTER JOIN的結果會增加一行,對應的class_name是NULL:

-- 先增加一列class_id=5:
INSERT INTO students (class_id, name, gender, score) values (5, '新生', 'M', 88);
-- 使用LEFT OUTER JOIN

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
LEFT OUTER JOIN classes c
ON s.class_id = c.id;

最後,我們使用FULL OUTER JOIN,它會把兩張表的所有記錄全部選擇出來,並且,自動把對方不存在的列填充為NULL:

-- 使用FULL OUTER JOIN

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
FULL OUTER JOIN classes c
ON s.class_id = c.id;

對於這麼多種JOIN查詢,到底什麼使用應該用哪種呢?其實我們用圖來表示結果集就一目瞭然了。

假設查詢語句是:

SELECT ... FROM tableA ??? JOIN tableB ON tableA.column1 = tableB.column2;


小結
JOIN查詢需要先確定主表,然後把另一個表的資料“附加”到結果集上;

INNER JOIN是最常用的一種JOIN查詢,它的語法是SELECT ... FROM <表1> INNER JOIN <表2> ON <條件...>;

JOIN查詢仍然可以使用WHERE條件和ORDER BY排序。