SQL之多表連接

這章主要講通過使用左連接,右連接,內連接,外連接及自然連接等方式進行多表查詢。例如要查詢人員的編號、姓名、部門編號及部門名字,只是通過人員表,是查不到部門名字的,只能查到部門編號,這就需要通過人員表的部門編號(外鍵)和部門表的部門編號(主鍵)進行關聯查詢。

表連接方式:

  • 交叉連接(笛卡爾積)cross joins
  • 自然連接 natural joins
  • 內連接 inner joins
  • 左外連接、右外連接及全外連接

在SQL:1999標準中連接表的語法:

clipboard

 

 

1、創建自然連接

  • 兩個表有相同列名才能創建自然連接,且不用添加連接條件
  • 把相同列名值相等的記錄連接起來,有多列相同的話,都會被連接起來
  • 列名相同,但是類型不同,連接時會報錯

例子:查詢部門編號、部門名稱、部門所在位置及城市

SQL> select department_id,department_name,location_id,city from departments natural join locations;

DEPARTMENT_ID DEPARTMENT_NAME                LOCATION_ID CITY

------------- ------------------------------ ----------- ------------------------------

           60 IT                                    1400 Southlake

 

 

2、使用using子句創建內連接

  • 當2個表有多列可以進行連接匹配的時候,可以使用using子句指定某一列進行匹配,可以用於列名相同但類型不同的列
  • 在using子句中指定的公共列,在整個語句中都不能使用表名或者表別名前綴進行限定,但是可以使用列別名
  • 沒有在using子句中指定,但是兩個表都有這個列,就必須要加上表名限定,同時最好使用列別名以便對結果進行區分
  • 如果列只出現在一個表中,表名限定可加可不加,但是如果加上,可以減少解析,提高性能
  • 使用using子句創建的連接爲內連接,不是自然連接,不能和自然連接同時使用

 

例子:查詢人員的編號、姓名、位置編號及部門編號,僅使用部門編號進行連接(不使用manager_id進行連接)

SQL> select employees.employee_id,employees.last_name,departments.location_id,department_id from employees join departments using (department_id);

EMPLOYEE_ID LAST_NAME                 LOCATION_ID DEPARTMENT_ID

----------- ------------------------- ----------- -------------

        200 Whalen                           1700            10

106 rows selected.

因爲人員表裏面有一條記錄的部門編號爲null,所有這裏只有106條結果

 

例子:使用表別名簡化書寫,減少解析,減少網絡傳輸量,提高你的性能

SQL> select e.employee_id,e.last_name,d.location_id,department_id from employees e join departments d using(department_id);

EMPLOYEE_ID LAST_NAME                 LOCATION_ID DEPARTMENT_ID

----------- ------------------------- ----------- -------------

        200 Whalen                           1700            10

 

 

3、使用on子句創建連接

  • 用於進行不同列名的連接,即使類型不同,也可以使用
  • 平時用得更多,也更容易理解
  • on子句中的列,在整個語句中必須使用表名或者表別名前綴進行限定
  • 後面可以使用and或者where進行條件限定

 

例子:使用on子句修改上面的例子

SQL> select e.employee_id,e.last_name,d.location_id,e.department_id,d.department_id from employees e join departments d on(e.department_id=d.department_id);

EMPLOYEE_ID LAST_NAME                 LOCATION_ID DEPARTMENT_ID DEPARTMENT_ID

----------- ------------------------- ----------- ------------- -------------

        200 Whalen                           1700            10            10

 

 

4、使用on子句創建自連接

自連接就是將一張表當成多張表進行連接,以人員表爲例,每一條記錄裏面有員工編號及對應的經理編號,如果想要查詢員工名字及對應的經理名字,那麼就要使用自連接,先將人員表作爲工作人員表,取出員工的名字及其經理的編號(外鍵),再將人員表作爲經理人員表,與其員工編號(主鍵)進行關聯,就可以找到經理名字了。

 

例子:查詢員工名字及其經理的名字

SQL> select e.last_name emp,e.manager_id,m.employee_id,m.last_name mgr

  2  from employees e join employees m

  3  on(e.manager_id=m.employee_id);

EMP                       MANAGER_ID EMPLOYEE_ID MGR

------------------------- ---------- ----------- -------------------------

Smith                            148         148 Cambrault

 

 

5、給連接增加額外的條件限定

 

(1)使用and進行條件限定

例子:查詢部門編號爲50的人員信息和部門信息

SQL> select e.employee_id,e.last_name,e.department_id,d.department_id

  2  from employees e join departments d

  3  on (e.department_id=d.department_id)

  4  and d.department_id=50;

EMPLOYEE_ID LAST_NAME                 DEPARTMENT_ID DEPARTMENT_ID

----------- ------------------------- ------------- -------------

        198 OConnell                             50            50

 

(2)使用where進行條件限定

例子:使用where改寫上面語句

SQL> select e.employee_id,e.last_name,e.department_id,d.department_id

  2  from employees e join departments d

  3  on (e.department_id=d.department_id)

  4  where d.department_id=50;

EMPLOYEE_ID LAST_NAME                 DEPARTMENT_ID DEPARTMENT_ID

----------- ------------------------- ------------- -------------

        198 OConnell                             50            50

 

 

6、使用on子句創建多表連接

運行順序是從下往上

 

 

例子:查詢人員編號,所在城市及部門名稱

SQL> select employee_id,city,department_name

  2  from employees e

  3  join departments d

  4  on e.department_id=d.department_id

  5  join locations l

  6  on d.location_id=l.location_id;

EMPLOYEE_ID CITY                           DEPARTMENT_NAME

----------- ------------------------------ ------------------------------

        100 Seattle                        Executive

 

 

7、不等連接

連接條件不是字段的值相等,而是不相等

 

例子:查詢人員的薪水等級

需要先創建薪水等級表並插入數據

創建表:

create table job_grades(

grade_level char(1),

lowest_sal number(10),

highest_sal number(10));

插入數據:

insert into job_grades values('A',1000,2999);

insert into job_grades values('B',3000,5999);

insert into job_grades values('C',6000,9999);

insert into job_grades values('D',10000,14999);

insert into job_grades values('E',15000,24999);

insert into job_grades values('F',25000,40000);

提交:

commit;

查詢結果:

select * from job_grades;

G LOWEST_SAL HIGHEST_SAL

- ---------- -----------

A       1000        2999

B       3000        5999

C       6000        9999

D      10000       14999

E      15000       24999

F      25000       40000

6 rows selected.

創建連接查詢

SQL> select e.last_name,e.salary,j.grade_level

  2  from employees e join job_grades j

  3  on e.salary between j.lowest_sal and j.highest_sal;

LAST_NAME                     SALARY G

------------------------- ---------- -

Olson                           2100 A

結果自動進行了排序

 

 

8、外連接

  • 在sql99標準裏面,兩張表進行連接,只返回兩個字段相匹配的記錄,叫inner join
  • 兩張表進行連接,除了inner join的結果,不匹配的也要取出來,根據方向,如果以左邊爲主,叫左連接,如果以右邊爲主,叫右連接,如果左邊右邊都要取出來,我們叫全外連接

 

(1)左連接

例子:查詢所有人員的部門編號及部門名稱,即使該人員不屬於任何部門

SQL> select e.last_name,e.department_id,d.department_name

  2  from employees e left join departments d

  3  on e.department_id=d.department_id;

LAST_NAME                 DEPARTMENT_ID DEPARTMENT_NAME

------------------------- ------------- ------------------------------

Whalen                               10 Administration

Grant

107 rows selected.

即使“Grant”這個人不屬於任何部門,也取出來了,但是部門編號和部門名稱爲null

 

(2)右連接

例子:查詢所有部門的部門編號、部門名稱及對應的人員,即使該部門沒有任何人員

SQL> 2

  2* from employees e left join departments d

SQL> c/left/right

  2* from employees e right join departments d

SQL> l

  1  select e.last_name,e.department_id,d.department_name

  2  from employees e right join departments d

  3* on e.department_id=d.department_id

SQL> r

  1  select e.last_name,e.department_id,d.department_name

  2  from employees e right join departments d

  3* on e.department_id=d.department_id

LAST_NAME                 DEPARTMENT_ID DEPARTMENT_NAME

------------------------- ------------- ------------------------------

Whalen                               10 Administration

                                        Payroll

122 rows selected.

總共122行,因爲部門表中有27個部門,人員表中有11個部門,故會多出16行,現在人員表有107行,除去沒有部門的一行,還有106行,106行加上16行就是122行。

SQL> select count(distinct department_id) from employees;

COUNT(DISTINCTDEPARTMENT_ID)

----------------------------

                          11

SQL> select count(distinct department_id) from departments;

COUNT(DISTINCTDEPARTMENT_ID)

----------------------------

                          27

 

(3)全外連接

例子:查詢所有人員對應的部門以及所有部門對應的人員

SQL> select e.last_name,e.department_id,d.department_name

  2  from employees e full join departments d

  3  on e.department_id=d.department_id;

LAST_NAME                 DEPARTMENT_ID DEPARTMENT_NAME

------------------------- ------------- ------------------------------

OConnell                             50 Shipping

Grant

                                        NOC

123 rows selected.

總共123行,將剛纔沒有部門的那一行也加上了

這個是sql99語法,那麼使用oracle自己的寫法

左連接:

SQL> select employee_id,last_name,e.department_id,d.department_id,department_name

  2  from employees e,departments d

  3  where e.department_id=d.department_id(+);

右連接:

SQL> select employee_id,last_name,e.department_id,d.department_id,department_name

  2  from employees e,departments d

  3  where e.department_id(+)=d.department_id;

那麼全外連接呢,

SQL> select employee_id,last_name,e.department_id,d.department_id,department_name

  2  from employees e,departments d

  3  where e.department_id(+)=d.department_id(+);

where e.department_id(+)=d.department_id(+)

                        *

ERROR at line 3:

ORA-01468: a predicate may reference only one outer-joined table

報錯,oracle自己語法中沒有全外連接的寫法,只能使用集合加起來。

用+來實現, 這個+可以這樣來理解:+表示補充,即哪個表有加號,這個表就是匹配表。所以加號寫在左表,右表就是全部顯示,故是右連接。

 

 

9、交叉連接(笛卡爾積)

  • 實際當中很少使用,主要用於測試時快速創建一張大表
  • 結果是2張錶行數的乘積

 

例子:交叉連接

SQL> select last_name,department_name from employees cross join departments;

Zlotkey                   Payroll

2889 rows selected.

看看這條語句的執行計劃:

SQL> explain plan

  2  for

  3  select employee_id,last_name,department_name

  4  from employees cross join departments

  5  ;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Plan hash value: 1162840532

------------------------------------------------------------------------------------

| Id  | Operation            | Name        | Rows  | Bytes | Cost (%CPU)| Time     |

------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT     |             |  2889 | 69336 |    41   (0)| 00:00:01 |

|   1 |  MERGE JOIN CARTESIAN|             |  2889 | 69336 |    41   (0)| 00:00:01 |

|   2 |   TABLE ACCESS FULL  | DEPARTMENTS |    27 |   324 |     3   (0)| 00:00:01 |

|   3 |   BUFFER SORT        |             |   107 |  1284 |    38   (0)| 00:00:01 |

|   4 |    TABLE ACCESS FULL | EMPLOYEES   |   107 |  1284 |     1   (0)| 00:00:01 |

------------------------------------------------------------------------------------

11 rows selected.

這裏兩張表進行全表掃描,然後進行笛卡爾積,執行計劃裏面,如果有笛卡爾積,去看一下,到底是什麼原因導致的,可以進行優化。

Oracle自己的語法處理笛卡爾積

SQL> select employee_id,last_name,department_name from employees,departments;

這裏兩張表直接加逗號,不加where條件,就是笛卡爾積,所以不要忘了加where條件。

 

 

10、總結

本章主要講了內連接,外連接以及笛卡爾積,內連接包含我們的自然連接,using子句,on子句,還有自連接,落在某個範圍。外連接包含我們的左連接,右連接,全外連接。

 

 

11、相關習題

(1)View the Exhibit and examine the structure of the ORDERS and ORDER_ITEMS tables. Evaluate the following SQL statement: SELECT oi.order_id, product_id, order_date FROM order_items oi JOIN orders o USING(order_id);Which statement is true regarding the execution of this SQL statement?

A.The statement would not execute because table aliases are not allowed in the JOIN clause.

B.The statement would not execute because the table alias prefix is not used in the USING clause.

C.The statement would not execute because all the columns in the SELECT clause are not prefixed with table aliases.

D.The statement would not execute because the column part of the USING clause cannot have a qualifier in the SELECT list.

 

答案:D

 

 

(2)View the Exhibit and examine the description of the ORDER_ITEMS and PRODUCT_INFORMATION tables. The ORDER_ITEM table has records pertaining to details for each product in an order. The PRODUCT_INFORMATION table has records for all the products available for ordering. Evaluate the following SQL statement: SELECT oi.order_id, pi.product_id FROM  order_items oi RIGHT OUTER JOIN product_information pi ON (oi.product_id=pi.product_id);Which statement is true regarding the output of this SQL statement?

A.The query would return the ORDER_ID and PRODUCT_ID for only those products that are ordered.

B.The query would return the ORDER_ID and PRODUCT_ID for the products that are ordered as well as for the products that have never been ordered.

C.The query would return the ORDER_ID and PRODUCT_ID for the products that are ordered but not listed in the PRODUCT_INFORMATION table.

D.The query would return the ORDER_ID and PRODUCT_ID for those products that are ordered as well as for the products that have never been ordered, and for the products that are not listed in  the PRODUCT_INFORMATION table.

 

答案:B

 

 

(3)View the Exhibit and examine the structure of the ORDER_ITEMS and ORDERS tables. You are asked to retrieve  the  ORDER_ID,  PRODUCT_ID,  and total  price  (UNIT_PRICE multiplied by QUANTITY),  where  the  total price  is  greater than 50,000.  You executed the  following SQL statement: SELECT order_id, product_id, unit_price*quantity "Total Price" FROM orde_items WHERE unit_price*quantity > 50000 NATURAL JOIN orders;Which statement is true regarding the execution of the statement?

A.The statement would execute and provide the desired result.

B.The statement would not execute because the ON keyword is missing in the NATURAL JOIN clause.

C.The statement would not execute because the WHERE clause is before the NATURAL JOIN clause.

D.The statement would not execute because the USING keyword is missing in the NATURAL JOIN clause.

 

答案:C

 

 

(4)View the Exhibit and examine the structure of the EMPLOYEES table. You want to display all employees and their managers having 100 as the MANAGER_ID. You want the output in two columns: the first column would have the LAST_NAME of the managers and the second column would have LAST_NAME of the employees. Which SQL statement would you execute?

A.SELECT m.last_name "Manager", e.last_name "Employee" FROM employees m JOIN employees e ON m.employee_id = e.manager_id WHERE m.manager_id=100;
B.SELECT m.last_name "Manager", e.last_name "Employee" FROM employees m JOIN employees e ON m.employee_id = e.manager_id WHERE e.manager_id=100;
C.SELECT m.last_name "Manager", e.last_name "Employee" FROM employees m JOIN employees e ON e.employee_id = m.manager_id WHERE m.manager_id=100;
D.SELECT m.last_name "Manager", e.last_name "Employee" FROM employees m JOIN employees e WHERE m.employee_id = e.manager_id AND e.manager_id=100;

 

答案:B

 

 

(5)View the Exhibit and examine the description of the DEPARTMENTS and EMPLOYEES tables. To retrieve data for all the employees for their EMPLOYEE_ID, FIRST_NAME, and DEPARTMENT NAME,  the  following  SQL  statement  was  written:  SELECT  employee_id,  first_name, department_name FROM employees NATURAL JOIN departments? The desired output is not obtained after executing the above SQL statement. What could be the reason for this?

A.The NATURAL JOIN clause is missing the USING clause.
B.The table prefix is missing for the column names in the SELECT clause.
C.The DEPARTMENTS table is not used before the EMPLOYEES table in the FROM clause.
D.The EMPLOYEES and DEPARTMENTS tables have more than one column with the same column name and data type.

 

答案:D

 

 

(6)View the Exhibit and examine the description of the EMPLOYEES and DEPARTMENTS tables. You want to display the LAST_NAME for the employees, LAST_NAME for the manager of the employees, and the DEPARTMENT_NAME for the employees having 100 as MANAGER_ID. The following SQL statement was written: SELECT m.last_name "Manager", e.last_name "Employee", department_name "Department" FROM employees m JOIN employees e ON (m.employee_id = e.manager_id)  WHERE  e.manager_id=100  JOIN  departments  d  ON  (e.department_id  =d.department_id)? Which statement is true regarding the output of this SQL statement?

A.The statement would provide the desired results.
B.The statement would not execute because the ON clause is written twice.
C.The statement would not execute because the WHERE clause is wrongly placed.
D.The statement would not execute because the self join uses the ON clause instead of the USING clause.

 

答案:C

 

 

(7)View the Exhibit and examine the structure for the ORDERS and ORDER_ITEMS tables. You want to display ORDER_ID, PRODUCT_ID, and TOTAL (UNIT_PRICE multiplied by QUANTITY) for all the orders placed in the last seven days. Which query would you execute ?

A.SELECT order_id, product_id, unit_price*quantity "TOTAL" FROM order_items oi JOIN orders o ON (o.order_id=oi.order_id) WHERE o.order_date>=SYSDATE-7 ;
B.SELECT o.order_id,oi.product_id, oi.unit_price*oi.quantity "TOTAL" FROM order_items oi JOIN orders o USING (order_id) WHERE o.order_date>=SYSDATE-7 ;
C.SELECT o.order_id, oi.product_id, oi.unit_price*oi.quantity "TOTAL" FROM order_items oi JOIN orders o WHERE o.order_date>=SYSDATE-7 ON (o.order_id=oi.order_id);
D.SELECT o.order_id, oi.product_id, oi.unit_price*oi.quantity "TOTAL" FROM order_items oi JOIN orders o ON (o.order_id=oi.order_id) WHERE o.order_date>=SYSDATE-7 ;

 

答案:D

 

 

(8)View the Exhibit and examine the table structure of DEPARTMENTS and LOCATIONS tables. You want to display all the cities that have no departments and the departments that have not been allocated cities. Which type of join between DEPARTMENTS and LOCATIONS tables would produce this information as part of its output?

A.NATURAL JOIN
B.FULL OUTER JOIN
C.LEFT OUTER JOIN
D.RIGHT OUTER JOIN

 

答案:B

 

 

(9)View  the  Exhibit  and  examine  the  structure  of  the  PRODUCT_INFORMATION  and INVENTORIES  tables.  You have  a  requirement  from  the  supplies  department  to give  a  list containing PRODUCT_ID, SUPPLIER_ID, and QUANTITY_ON_HAND for all the products wherein QUANTITY_ON_HAND is less than five. Which two SQL statements can accomplish the task?(Choose two.)

A.SELECT  product_id,  quantity_on_hand  ,  supplier_id  FROM product_information  NATURAL  JOIN inventories AND quantity_on_hand < 5; 
B.SELECT  i.product_id,  i.quantity_on_hand  ,  pi.supplier_id  FROM  product_information  pi  JOIN inventories i USING (product_id) AND quantity_on_hand < 5 ;
C.SELECT  i.product_id,  i.quantity_on_hand  ,  pi.supplier_id  FROM product_information  pi  JOIN inventories i ON (pi.product_id=i.product_id) WHERE quantity_on_hand < 5 ;
D.SELECT  i.product_id,  i.quantity_on_hand  ,  pi.supplier_id  FROM product_information  pi  JOIN inventories i ON (pi.product_id=i.product_id) AND quantity_on_hand < 5  ;

 

答案:CD

 

 

(10)View the  Exhibit  and  examine  the  structure  of  the  PRODUCT_INFORMATION  and INVENTORIES tables. You want to display the quantity on hand for all the products available in the PRODUCT_INFORMATION  table  that  have  the  PRODUCT_STATUS  as  'orderable'. QUANTITY_ON_HAND is a column in the INVENTORIES table. The following SQL statement was written to accomplish the task: SELECT pi.product_id, pi.product_status, sum(i.quantity_on_hand) FROM product_information pi LEFT OUTER JOIN inventories i ON (pi.product_id = i.product_id) WHERE (pi.product_status  =  'orderable')  GROUP  BY  pi.product_id,  pi.product_status;Which statement is true regarding the execution of this SQL statement?

A.The statement would execute and produce the desired output.
B.The statement would not execute because the WHERE clause is used before the GROUP BY clause.
C.The statement would not execute because prefixing table alias to column names is not allowed with the ON clause.
D.The statement would not execute because the WHERE clause is not allowed with LEFT OUTER JOIN.

 

答案:A

 

 

(11)Which two statements are true regarding the types of table joins available in Oracle Database 10g?(Choose two.)

A.You can use the JOIN clause to join only two tables.
B.You can explicitly provide the join condition with a NATURAL JOIN.
C.You can use the USING clause to join tables on more than one column.
D.You can use the ON clause to specify multiple conditions while joining tables.

 

答案:CD

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章