2.15 使用Order by子句对行进行排序
SELECT * from CUS ORDER BY last_name;默认情况下,Order by子句会按照升序对数据进行排序(较小的值先出现),可使用Describe关键字指定按照降序对数据进行排序(较大的值先出现),也可以使用ASC关键字显式地说明采用升序进行排序(升序是默认设置)。
下面的SQL,对CUS表先根据first_name进行升序,然后再根据last_name进行降序排序
SELECT * from CUS ORDER BY FIRST_NAME ASC ,last_name DESC;
在Order by子句中,也可以根据列的次序指定对哪一列(也可以使用多列)进行排序:1表示按第一列,2表示按第2列,依次类推。如下:
SELECT * from CUS ORDER BY 2 ASC ,3 DESC;(和上述查询结果一致)
2.16 执行使用两个表的Select语句
数据库模式有多个表,这些表分别用于保存不同的数据。实际应用中,常常需要从多个表中检索信息——例如,检索产品类型名和实际产品的名称并一起显示。
例如,想要获得产品#3的名称及其产品类型的名称。产品的名称在products表的name列中,类型在products_types表的name中。这俩张表通过一个外键列product_type_id彼此关联在一起。products表的product_type_id列(外键)指向product_types表的product_type_id列(主键)。在查询中使用表连接可以完成上述任务。要在查询中将两个表连接起来,就需要在查询的From子句中同时指定2个表(From products,product_types),Where子句中指明两个表中的相关列(进行过滤)。一般情况下,连接中使用的列时一个表的主键和另一个表的外键。因为在连接条件中使用等于操作符(=),所以这种连接称为等连接(equijoin)。查询如下:
SELECT PRODUCTS."NAME" Product_Name,PRODUCT_TYPES."NAME"Product_Type from PRODUCTS,PRODUCT_TYPES
where PRODUCTS.PRODUCT_TYPE_ID=PRODUCT_TYPES.PRODUCT_TYPE_ID
AND PRODUCTS.PRODUCT_ID=3;
2.17 使用表别名
表别名要在From子句中指定,且别名位于查询中其余列之前,下面的查询使用p作为products表的别名,pt作为product_types表的别名。
SELECT p."NAME",PT.name from PRODUCTS p,PRODUCT_TYPES pt WHERE p.PRODUCT_TYPE_ID=PT.PRODUCT_TYPE_ID ORDER BY p."NAME";
2.18 笛卡尔积
如果在多表查询中不指定连接条件,就会导致将一个表中的所有行都连接到另外一个表中的所有行上,这种情况就称为笛卡尔积(Cartesian product)。假设第一个表有50行,第二个表有100行。如果从这两个表中不使用连接条件来检索行,那么就会返回50*100=5000行。
2.19 执行使用多于两个表的select语句
连接可以用于连接任意多个表。连接数=查询中使用的表的总数-1。
现在考虑一个更复杂的例子,这个例子涉及4个表,因此需要使用3个连接。假设希望查询以下信息:
1)已购买过产品的顾客(来自purchasees表)
2)顾客的姓名(来自cus表)
3)顾客购买的产品名(来自products表)
4)产品类型名(来自product_types表)
完整的SQL为:
select c.first_name,c.last_name,p.name AS PRODUCT,pt.name AS TYPE from CUS c,purchases pr,products p,product_types pt
Where c.customer_id=pr.customer_id
AND p.product_id = pr.product_id
AND p.product_type_id=pt.product_type_id
Order by p.name;
2.20 连接条件和连接类型
连接条件(join condition)可以分为两类:
·等连接(equijoin)在连接中使用等于操作符(=)
·不等连接(non-equijoin):在连接中使用除等号之外的操作符,例如<、>、Between等等。
·内连接(inner join):只有当连接中的列包含满足连接条件的值时才会返回一行。这就是说,如果某一行的连接条件中的一列是空值(Null),那么这行就不会返回。目前为止看到的都是内连接的例子。
·外连接(outer join):即使连接条件中的一列包含空值也会返回一行。
·自连接(self join):返回连接到同一个表中的行。
2.20.1不等连接
不等连接在连接中使用除等于操作符之外的操作符,包括不等于(<>)、大于(>)、小于(<)、小于等于(<=)、大于等于(>=)、Like、IN和Between。很少碰到使移动不等连接的情况,但偶尔也会碰到,在那样的情况下就需要使用Between操作符。
使用不等连接来检索员工的工资和工资等级,工资等级用Between操作符来确定:
SELECT e.FIRST_NAME,e.LAST_NAME,e.TITLE,e.SALARY,SG.SALARY_GRADE_ID
FROM EMPLOYEES e,SALARY_GRADES sg
WHERE e.SALARY BETWEEN SG.LOW_SALARY AND SG.HIGH_SALARY
ORDER BY SALARY_GRADE_ID
在此查询中,如果员工的工资在工资等级的范围内,则Between操作符返回true。
2.20.2外连接
外连接(OUTER JOIN):外连不但返回符合连接和查询条件的数据行,还返回不符合条件的一些行。外连接分三类:左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)和全外连接(FULL OUTER JOIN)。
三者的共同点是都返回符合连接条件和查询条件(即:内连接)的数据行。不同点如下:
左外连接还返回左表中不符合连接条件单符合查询条件的数据行。
右外连接还返回右表中不符合连接条件单符合查询条件的数据行。
全外连接还返回左表中不符合连接条件单符合查询条件的数据行,并且还返回右表中不符合连接条件单符合查询条件的数据行。全外连接实际是上左外连接和右外连接的数学合集(去掉重复),即“全外=左外 UNION 右外”。
说明:左表就是在“(LEFT OUTER JOIN)”关键字左边的表。右表当然就是右边的了。在三种类型的外连接中,OUTER 关键字是可省略的。
在外连接中,即使连接中的列包含一个空值,外连接也会返回一行。在连接条件中可以使用外连接操作符来执行一个外连接:Oracle特有的外连接操作符是使用圆括号括起来的加号(+)。
在下面的查询中,注意Oracle的外连接操作符(+)位于product表中product_type_id列(包含空值的列)相反的一边,也就是(+)在不为null的一边:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p,PRODUCT_TYPES pt
WHERE p.PRODUCT_TYPE_ID=PT.PRODUCT_TYPE_ID(+);
这样就可以查到即使连接条件中,不带有(+)的列值即使为null也能出现在查询结果中。
试与上句SQL比较查询结果的不同:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p,PRODUCT_TYPES pt
WHERE p.PRODUCT_TYPE_ID(+)=PT.PRODUCT_TYPE_ID;
1、左外连接和右外连接
外连接分为两类:
·左外连接和右外连接。
对于二者区别,考虑以下区别:
select ……
From table1,table2
……
假设这俩表对table1.column1和table2.column2进行连接,且table1中包含column1为空值的一行。要执行一个左外连接,where子句如下:
Where table1.column1 = table2.column2(+);--左连接:等号左边的列值可以为空,该条记录仍然出现在查询结果中。
右连接的where子句:
Where table1.column1 (+)= table2.column2;--右连接:等号右边的列值可以为空,该条记录仍然出现在查询结果中。
可想而知,如果table1和table2都包含其中一个为空值的行,则根据使用的是左外连接还是右外连接,会得到不同的结果。
2.外连接的限制
外连接的使用有一些限制:
1)只能在连接的一端使用外连接操作符,而不能在两端同时使用外连接操作符;
2)不能同时使用外连接条件和IN/OR操作符:where p.product_type_id (+) IN(1,2,3,4);
2.20.3 自连接
自连接是对同一个表进行连接。要执行一个自连接,必须使用不同的表别名来标识在查询中每次对表的引用(形式上相当于两个表)。
现在考虑一个例子:employees表中有一列manager_id,它包含每个员工管理者的employee_id,如果员工没有管理者,则manager_id是空值。
可以使用一个自连接来显示每个雇员及其管理者的名字。下面的例子中employees表被引用了两次,分别使用了两个别名:w(worker)和m(manager)。别名w用于获得员工的姓名,而别名m则用于获得管理者的姓名。自连接是对w.mananger_id和m.employee_id进行的:
CEO是James Smith
SELECT
W.FIRST_NAME || ' '|| W.LAST_NAME || 'works for ' || M .FIRST_NAME || ' ' || M .LAST_NAME
FROM
EMPLOYEES W,
EMPLOYEES M
WHERE
W.MANAGER_ID = M.EMPLOYEE_ID
ORDER BY
W.FIRST_NAME;
2.21 使用SQL/92语法执行连接
目前为止我们看到的连接都是使用Oracle的连接语法。Oracle语法的基础是ANSI SQL/86标准。在开发Oracle Database 9i时,数据库还实现了ANSI SQL/92标准的连接语法,应该在查询中使用SQL/92标准的语法。
2.21.1 使用SQL/92标准语法执行两个表的内连接
之前已经提到过下面这个SQL
SELECT p."NAME",PT.name from PRODUCTS p,PRODUCT_TYPES pt WHERE p.PRODUCT_TYPE_ID=PT.PRODUCT_TYPE_ID ORDER BY p."NAME";
SQL/92引入了Inner Join和ON子句来执行内连接。下面使用Inner join和ON子句重写上面的查询:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p INNER JOIN PRODUCT_TYPES ptON p.PRODUCT_TYPE_ID=PT.PRODUCT_TYPE_ID
ORDER BY p."NAME";
不等连接操作符和ON子句可以同时使用。
SELECT e.FIRST_NAME,e.LAST_NAME,e.TITLE,e.SALARY,SG.SALARY_GRADE_ID
FROM EMPLOYEES e,SALARY_GRADES sg
WHERE e.SALARY BETWEEN SG.LOW_SALARY AND SG.HIGH_SALARY
ORDER BY SALARY_GRADE_ID。
下面使用SQL/92标准重写这个查询:
SELECT e.FIRST_NAME,e.LAST_NAME,e.TITLE,e.SALARY,SG.SALARY_GRADE_ID
FROM EMPLOYEES e INNER JOIN SALARY_GRADES sg
ON e.SALARY BETWEEN SG.LOW_SALARY AND SG.HIGH_SALARY
ORDER BY SALARY_GRADE_ID;
2.21.2使用USING关键字简化连接
SQL/92标准可以使用Using子句对连接条件进一步简化,前提是查询满足以下限制:
1)查询必须是等连接的
2)等连接中的列必须同名
我们将要执行的大部分连接都是等连接,如果总是对主键和外键使用相同的名字,就可以满足上面的这些限制。
下面的查询使用Using子句代替ON子句:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p INNER JOIN PRODUCT_TYPES pt
ON p.PRODUCT_TYPE_ID=PT.PRODUCT_TYPE_ID
ORDER BY p."NAME";
改写:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p INNER JOIN PRODUCT_TYPES pt
Using (PRODUCT_TYPE_ID)
ORDER BY p."NAME";
此时,如果希望查看product_type_id的值,则在select子句中只能指定该列名,不能使用表名或别名:
SELECT p."NAME",PT."NAME" , PRODUCT_TYPE_ID FROM PRODUCTS p INNER JOIN PRODUCT_TYPES pt
Using (PRODUCT_TYPE_ID)
ORDER BY p."NAME";
2.21.3 使用SQL/92执行多于两个表的内连接
select c.first_name,c.last_name,p.name AS PRODUCT,pt.name AS TYPE from CUS c,purchases pr,products p,product_types pt
Where c.customer_id=pr.customer_id
AND p.product_id = pr.product_id
AND p.product_type_id=pt.product_type_id
Order by p.name;
重写:
SELECT
c.first_name,
c.last_name,
P . NAME,
pt. NAME AS TYPE
FROM
CUS c
INNER JOIN PURCHASES pr USING (CUSTOMER_ID)
INNER JOIN PRODUCTS P USING (PRODUCT_ID)
INNER JOIN PRODUCT_TYPES pt USING (PRODUCT_TYPE_ID)
ORDER BY
P . NAME;
多表连接实际上就是先对两个表进行连接,再将结果与另一个表进行连接,依次类推。
2.21.4 使用SQL/92执行多列的内连接
如果连接使用了两个表中的多个列,那么就可以在ON子句中使用AND操作符逐一列出这些列。示例如下:
From tb1 Inner join tb2
On tb1.col1 =tb2.col1
AND tb1.col2 = tb2.col2
可以使用USING子句进一步简化,条件是执行等连接,而且列名相同。如下:
From tb1 Inner join tb2
using (col1,col2);
2.21.5 使用SQL/92执行外连接
前文中已经看到过如何使用外连接操作符(+)执行外连接,外连接操作符(+)是Oracle特有的语法。SQL/92使用一个不同的语法来执行外连接。SQL/92不使用(+)操作符,而是在select语句的from子句中指定连接类型,语法如下:
From table1 {LEFT | Right | FULL} OUTER JOIN table2;
其中:
1)table1和table2指定了希望连接的表;
2)LEFT说明希望执行左外连接
3)Right说明希望执行右外连接
4)Full说明希望执行全外连接;全外连接使用table1和table2中所有的行,包括连接列为空值的行。不能使用(+)操作符直接执行全外连接。
1.执行左外连接
下面看一下之前提到过的使用Oracle专有的(+)操作符执行一个左外连接:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p,PRODUCT_TYPES pt
WHERE PT.PRODUCT_TYPE_ID=p.PRODUCT_TYPE_ID(+) order By p.name;
下面使用SQL/92的Left outer Join关键字对这个查询进行重写:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p LEFT OUTER JOIN PRODUCT_TYPES pt
Using (PRODUCT_TYPE_ID)
ORDER BY p."NAME";
2.执行右外连接---Right outer join
before:对表products执行右外连接。
SELECT p."NAME",PT."NAME" FROM PRODUCTS p,PRODUCT_TYPES pt
WHERE p.PRODUCT_TYPE_ID(+) =PT.PRODUCT_TYPE_ID order By p.name;
after:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p RIGHT OUTER JOIN PRODUCT_TYPES pt
using (PRODUCT_TYPE_ID) ORDER BY p."NAME";
3.执行全外连接
全外连接使用连接表中的所有行,包括连接中使用的列为空值的那些行。这样一来,不管是那个表中的对应连接条件中的列值为空值,都出现在结果集中。下面是一个SQL/92的Full Outer Join关键字的查询:
SELECT p."NAME",PT."NAME" FROM PRODUCTS p full outer join PRODUCT_TYPES pt
using(PRODUCT_TYPE_ID) ORDER BY p."NAME";
结果:
Hack Empire DVD
Hero DVD
Massage Ball Null
Null Book
适用场景??
2.21.6 使用SQL/92执行自连接
先看SQL/86标准执行自连接的语法
SELECT
W.FIRST_NAME || ' '|| W.LAST_NAME || 'works for ' || M .FIRST_NAME || ' ' || M .LAST_NAME
FROM
EMPLOYEES W,
EMPLOYEES M
WHERE
W.MANAGER_ID = M.EMPLOYEE_ID
ORDER BY
W.FIRST_NAME
重写:
SELECT
W.FIRST_NAME || ' ' || W.LAST_NAME || 'works for ' || M .FIRST_NAME || ' ' || M .LAST_NAME
FROM
EMPLOYEES W
INNER JOIN EMPLOYEES M ON W.MANAGER_ID = M .EMPLOYEE_ID
ORDER BY
W.FIRST_NAME;
2.21.7使用SQL/92执行交叉连接
前文已经提到如果忽略了两个表之间的连接条件会导致笛卡尔积。通过使用SQL/92的连接语法可以避免产生笛卡尔积,因为在对表进行连接时,通常必须通过一个ON或Using子句。
如果的确要使用笛卡尔积,SQL/92标准要求必须在查询中使用Cross Join关键字显式声明。
下面的查询中,就使用Cross Join关键字在product_types和products表之间生成一个笛卡尔积:
Select *
From product_types Cross Join products;
小结:使用SQL/92标准的连接时,注意JOIN 与ON/Using匹配的。