SQL之多表連線
這章主要講通過使用左連線,右連線,內連線,外連線及自然連線等方式進行多表查詢。例如要查詢人員的編號、姓名、部門編號及部門名字,只是通過人員表,是查不到部門名字的,只能查到部門編號,這就需要通過人員表的部門編號(外來鍵)和部門表的部門編號(主鍵)進行關聯查詢。
表連線方式:
- 交叉連線(笛卡爾積)cross joins
- 自然連線 natural joins
- 內連線 inner joins
- 左外連線、右外連線及全外連線
在SQL:1999標準中連線表的語法:
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