top
Loading...
13.2.7.1.JOIN語法
13.2.7.1. JOIN語法

MySQL支持以下JOIN語法。這些語法用于SELECT語句的table_references部分和多表DELETEUPDATE語句:

table_references:
    table_reference [, table_reference] …

table_reference:
    table_factor
  | join_table

table_factor:
    tbl_name [[AS] alias]
        [{USE|IGNORE|FORCE} INDEX (key_list)]
  | ( table_references )
  | { OJ table_reference LEFT OUTER JOIN table_reference
        ON conditional_expr }

join_table:
    table_reference [INNER | CROSS] JOIN table_factor [join_condition]
  | table_reference STRAIGHT_JOIN table_factor
  | table_reference STRAIGHT_JOIN table_factor ON condition
  | table_reference LEFT [OUTER] JOIN table_reference join_condition
  | table_reference NATURAL [LEFT [OUTER]] JOIN table_factor
  | table_reference RIGHT [OUTER] JOIN table_reference join_condition
  | table_reference NATURAL [RIGHT [OUTER]] JOIN table_factor

join_condition:
    ON conditional_expr
  | USING (column_list)

一個表引用還被稱為一個聯合表達式。

SQL標準相比,table_factor的語法被擴展了。SQL標準只接受table_reference,而不是圓括號內的一系列條目。

如果我們把一系列table_reference條目中的每個逗號都看作相當于一個內部聯合,則這是一個穩妥的擴展。例如:

SELECT * FROM t1 LEFT JOIN (t2, t3, t4)
                 ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)

相當于:

SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)
                 ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)

MySQL中,CROSS JOIN從語法上說與INNER JOIN等同(兩者可以互相替換。在標準SQL中,兩者是不等同的。INNER JOINON子句同時使用,CROSS JOIN以其它方式使用。

通常,在只含有內部聯合運行的聯合表達式中,圓括號可以被忽略。MySQL也支持嵌套的聯合(見7.2.10節,“MySQL如何優化嵌套Join”)。

通常,您不應對ON部分有任何條件。ON部分用于限定在結果集合中您想要哪些行。但是,您應在WHERE子句中指定這些條件。這條規則有一些例外。

在前面的清單中顯示的{ OJ ... LEFT OUTER JOIN ...}語法的目的只是為了保持與ODBC的兼容性。語法中的花括號應按字面書寫;該括號不是中間語法。中間語法用于語法描述的其它地方。

·         表引用可以使用tbl_name AS alias_nametbl_name alias_name指定別名:

·                mysql> SELECT t1.name, t2.salary FROM employee AS t1, info AS t2
·                    ->        WHERE t1.name = t2.name;
·                mysql> SELECT t1.name, t2.salary FROM employee t1, info t2
·                    ->        WHERE t1.name = t2.name;

·         ON條件句是可以被用于WHERE子句的格式的任何條件表達式。

·         如果對于在LEFT JOIN中的ONUSING部分中的右表沒有匹配的記錄,則所有列被設置為NULL的一個行被用于右表。如果一個表在其它表中沒有對應部分,您可以使用這種方法在這種表中查找記錄:

·                mysql> SELECT table1.* FROM table1
·                    ->        LEFT JOIN table2 ON table1.id=table2.id
·                    ->        WHERE table2.id IS NULL;

本例查找在table1中含有一個id值的所有行。同時,在table2中沒有此id值(即,table1中的所有行在table2中沒有對應的行)。本例假設table2.id被定義為NOT NULL。請參見7.2.9節,“MySQL如何優化LEFT JOIN和RIGHT JOIN”。

·         USING(column_list)子句用于為一系列的列進行命名。這些列必須同時在兩個表中存在。如果表a和表b都包含列c1, c2c3,則以下聯合會對比來自兩個表的對應的列:

·                a LEFT JOIN b USING (c1,c2,c3)

·         兩個表的NATURAL [LEFT] JOIN被定義為與INNER JOIN語義相同,或與使用USING子句的LEFT JOIN語義相同。USING子句用于為同時存在于兩個表中的所有列進行命名。

·         INNER JOIN和,(逗號)在無聯合條件下是語義相同的:兩者都可以對指定的表計算出笛卡兒乘積(也就是說,第一個表中的每一行被聯合到第二個表中的每一行)。

·         RIGHT JOIN的作用與LEFT JOIN的作用類似。要使代碼可以在數據庫內移植,建議您使用LEFT JOIN代替RIGHT JOIN

·         STRAIGHT_JOINJOIN相同。除了有一點不一樣,左表會在右表之前被讀取。STRAIGH_JOIN可以被用于這樣的情況,即聯合優化符以錯誤的順序排列表。

您可以提供提示,當從一個表中恢復信息時,MySQL應使用哪個索引。通過指定USE INDEXkey_list),您可以告知MySQL只使用一個索引來查找表中的行。另一種語法IGNORE INDEXkey_list)可以被用于告知MySQL不要使用某些特定的索引。如果EXPLAIN顯示MySQL正在使用來自索引清單中的錯誤索引時,這些提示會有用處。

您也可以使用FORCE INDEX,其作用接近USE INDEXkey_list),不過增加了一項作用,一次表掃描被假設為代價很高。換句話說,只有當無法使用一個給定的索引來查找表中的行時,才使用表掃描。

USE KEYIGNORE KEYFORCE KEYUSE INDEXIGNORE INDEXFORCE INDEX的同義詞。

注釋:當MySQL決定如何在表中查找行并決定如何進行聯合時,使用USE INDEXIGNORE INDEXFORCE INDEX只會影響使用哪些索引。當分解一個ORDER BYGROUP BY時,這些語句不會影響某個索引是否被使用。

部分的聯合示例:

mysql> SELECT * FROM table1,table2 WHERE table1.id=table2.id;
mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id;
mysql> SELECT * FROM table1 LEFT JOIN table2 USING (id);
mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id
    ->          LEFT JOIN table3 ON table2.id=table3.id;
mysql> SELECT * FROM table1 USE INDEX (key1,key2)
    ->          WHERE key1=1 AND key2=2 AND key3=3;
mysql> SELECT * FROM table1 IGNORE INDEX (key3)
    ->          WHERE key1=1 AND key2=2 AND key3=3;

見7.2.9節,“MySQL如何優化LEFT JOIN和RIGHT JOIN”。

注釋:自然聯合和使用USING的聯合,包括外部聯合變量,依據SQL:2003標準被處理。這些變更時MySQL與標準SQL更加相符。不過,對于有些聯合,這些變更會導致不同的輸出列。另外,有些查詢在舊版本(5.0.12以前)工作正常,但也必須重新編寫,以符合此標準。對于有關當前聯合處理和舊版本中的聯合處理的效果的對比,以下列表提供了更詳細的信息。

·         NATURAL聯合或USING聯合的列會與舊版本不同。特別是,不再出現冗余的輸出列,用于SELECT *擴展的列的順序會與以前不同。

示例:

CREATE TABLE t1 (i INT, j INT);
CREATE TABLE t2 (k INT, j INT);
INSERT INTO t1 VALUES(1,1);
INSERT INTO t2 VALUES(1,1);
SELECT * FROM t1 NATURAL JOIN t2;
SELECT * FROM t1 JOIN t2 USING (j);

對于舊版本,語句會產生以下輸出:

+------+------+------+------+
| i    | j    | k    | j    |
+------+------+------+------+
|    1 |    1 |    1 |    1 |
+------+------+------+------+
+------+------+------+------+
| i    | j    | k    | j    |
+------+------+------+------+
|    1 |    1 |    1 |    1 |
+------+------+------+------+

在第一個SELECT語句中,列i同時出現在兩個表中,為一個聯合列,所以,依據標準SQL,該列在輸出中只出現一次。與此類似,在第二個SELECT語句中,列jUSING子句中被命名,應在輸出中只出現一次。但是,在兩種情況下,冗余的列均沒被消除。另外,依據標準SQL,列的順序不正確。

現在,語句產生如下輸出:

+------+------+------+
| j    | i    | k    |
+------+------+------+
|    1 |    1 |    1 |
+------+------+------+
+------+------+------+
| j    | i    | k    |
+------+------+------+
|    1 |    1 |    1 |
+------+------+------+

冗余的列被消除,并且依據標準SQL,列的順序是正確的:

o        第一,兩表共有的列,按在第一個表中的順序排列

o        第二,第一個表中特有的列,按該表中的順序排列

o        第三,第二個表中特有的列,按該表中的順序排列

·         對多方式自然聯合的估算會不同。方式要求重新編寫查詢。假設您有三個表t1(a,b), t2(c,b)t3(a,c),每個表有一行:t1(1,2), t2(10,2)t3(7,10)。同時,假設這三個表具有NATURAL JOIN

·                SELECT  FROM t1 NATURAL JOIN t2 NATURAL JOIN t3;

在舊版本中,第二個聯合的左操作數被認為是t2,然而它應該為嵌套聯合(t1 NATURAL JOIN t2)。結果,對t3的列進行檢查時,只檢查其在t2中的共有列。如果t3t1有共有列,這些列不被用作equi-join列。因此,在舊版本的MySQL中,前面的查詢被轉換為下面的equi-join

SELECT  FROM t1, t2, t3
  WHERE t1.b = t2.b AND t2.c = t3.c;

此聯合又省略了一個equi-join謂語(t1.a = t3.a)。結果是,該聯合產生一個行,而不是空結果。正確的等價查詢如下:

SELECT  FROM t1, t2, t3
  WHERE t1.b = t2.b AND t2.c = t3.c AND t1.a = t3.a;

如果您要求在當前版本的MySQL中獲得和舊版本中相同的查詢結果,應把自然聯合改寫為第一個equi-join

·         在舊版本中,逗號操作符(,)和JOIN均有相同的優先權,所以聯合表達式t1, t2 JOIN t3被理解為((t1, t2) JOIN t3)。現在,JOIN有更高的優先權,所以表達式被理解為(t1, (t2 JOIN t3))。這個變更會影響使用ON子句的語句,因為該子句只參閱聯合操作數中的列。優先權的變更改變了對什么是操作數的理解。

示例:

CREATE TABLE t1 (i1 INT, j1 INT);
CREATE TABLE t2 (i2 INT, j2 INT);
CREATE TABLE t3 (i3 INT, j3 INT);
INSERT INTO t1 VALUES(1,1);
INSERT INTO t2 VALUES(1,1);
INSERT INTO t3 VALUES(1,1);
SELECT * FROM t1, t2 JOIN t3 ON (t1.i1 = t3.i3);

在舊版本中,SELECT是合法的,因為t1, t2被隱含地歸為(t1,t2)。現在,JOIN取得了優先權,因此用于ON子句的操作數是t2t3。因為t1.i1不是任何一個操作數中的列,所以結果是出現在'on clause'中有未知列't1.i1'的錯誤。要使聯合可以被處理,用使用圓括號把前兩個表明確地歸為一組,這樣用于ON子句的操作數為(t1,t2)t3

SELECT * FROM (t1, t2) JOIN t3 ON (t1.i1 = t3.i3);

本變更也適用于INNER JOINCROSS JOINLEFT JOINRIGHT JOIN

·         在舊版本中,ON子句可以參閱在其右邊命名的表中的列。現在,ON子句只能參閱操作數。

示例:

CREATE TABLE t1 (i1 INT);
CREATE TABLE t2 (i2 INT);
CREATE TABLE t3 (i3 INT);
SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3;

在舊版本中,SELECT語句是合法的。現在該語句會運行失敗,出現在'on clause'中未知列'i3'的錯誤。這是因為i3t3中的一個表,而t3不是ON子句中的操作數。本語句應進行如下改寫:

SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);

·         在舊版本中,一個USING子句可以被改寫為一個ON子句。ON子句對比了相應的列。例如,以下兩個子句具有相同的語義:

·                a LEFT JOIN b USING (c1,c2,c3)
·                a LEFT JOIN b ON a.c1=b.c1 AND a.c2=b.c2 AND a.c3=b.c3

現在,這兩個子句不再是一樣的:

o        在決定哪些行滿足聯合條件時,兩個聯合保持語義相同。

o        在決定哪些列顯示SELECT *擴展時,兩個聯合的語義不相同。USING聯合選擇對應列中的合并值,而ON聯合選擇所有表中的所有列。對于前面的USING聯合,SELECT *選擇這些值:

o                     COALESCE(a.c1,b.c1), COALESCE(a.c2,b.c2), COALESCE(a.c3,b.c3)

對于ON聯合,SELECT *選擇這些值:

a.c1, a.c2, a.c3, b.c1, b.c2, b.c3

使用內部聯合時,COALESCE(a.c1,b.c1)a.c1b.c1相同,因為兩列將具有相同的值。使用外部聯合時(比如LEFT JOIN),兩列中有一列可以為NULL。該列將會從結果中被忽略。

作者:mysql.com
來源:http://dev.mysql.com/doc/refman/5.1/zh/sql-syntax.html
北斗有巢氏 有巢氏北斗