SQL除了可以对列名和计算字段使用别名,还允许给表名起别名,这样做有两个主要理由:
下例是使用了别名的SELECT语句:
mysql> SELECT cust_name,cust_contact
-> FROM Customers AS C, Orders AS O,OrderItems AS OI
-> WHERE C.cust_id = O.cust_id AND OI.order_num = O.order_num AND prod_id = 'RGAN01';
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
2 rows in set (0.01 sec)
至今为止,我们使用的只是内联结或等值联结这些简单联结,接下来要看三种其他的联结:
如前所述,使用表别名的一个主要原因,是能在一条SELECT语句中不止一次引用相同的表。
下面举一个例子:
假如要给Jim Jones同一公司的所有顾客发送一封信件,这个查询要求首先找出Jim Jones工作的公司,然后找出在该公司工作的所有顾客,下面是解决此问题的一个方法:
mysql> SELECT cust_id,cust_name,cust_contact
-> FROM customers
-> WHERE cust_name =
-> (SELECT cust_name FROM customers WHERE cust_contact = 'Jim Jones');
+------------+-----------+--------------------+
| cust_id | cust_name | cust_contact |
+------------+-----------+--------------------+
| 1000000003 | Fun4All | Jim Jones |
| 1000000004 | Fun4All | Denise L. Stephens |
+------------+-----------+--------------------+
2 rows in set (0.00 sec)
这个是第一种解决方案,使用了子查询。
下面是使用联结的相同查询:
mysql> SELECT c1.cust_name,c1.cust_id,c1.cust_contact
-> FROM Customers AS c1,Customers AS c2
-> WHERE c1.cust_name = c2.cust_name AND c2.cust_contact = 'Jim Jones';
+-----------+------------+--------------------+
| cust_name | cust_id | cust_contact |
+-----------+------------+--------------------+
| Fun4All | 1000000003 | Jim Jones |
| Fun4All | 1000000004 | Denise L. Stephens |
+-----------+------------+--------------------+
2 rows in set (0.00 sec)
也可使用Inner Join的形式:
mysql> SELECT c1.cust_name,c1.cust_id,c1.cust_contact
-> FROM Customers AS c1 INNER JOIN Customers AS c2
-> ON c1.cust_name = c2.cust_name AND c2.cust_contact = 'Jim Jones';
+-----------+------------+--------------------+
| cust_name | cust_id | cust_contact |
+-----------+------------+--------------------+
| Fun4All | 1000000003 | Jim Jones |
| Fun4All | 1000000004 | Denise L. Stephens |
+-----------+------------+--------------------+
2 rows in set (0.00 sec)
这里的查询,实际上是将两张相同的表进行联结,并使其中一张表限定了Jim Jones的名字,在这种情况下,需要使用别名。而一旦在这种情况使用别名,则SELECT后所接的列名也必须写成完全形式,否则两个表的列名是相同的,会出错。
自联结通常作为外部语句,用来替代从相同的表中检索数据的使用子查询语句,虽然最终结果是相同的,但是很多DBMS处理联结是远比子查询快的。
无论何时对表进行联结,应该至少有一列不止出现在一个表中,标准的联结返回的是所有数据,相同的列甚至多次出现。
而自然联结排除多次出现,使每一列只出现一次。怎样完成这件工作呢?答案是,系统不完成这样的工作,而要由我们自己来完成,自然联结要求我们选择那些唯一的列,一般通过对一个表使用通配符,而对其他的表的列使用明确的子集来完成。
下面举一个例子:
mysql> SELECT C.*,O.order_num,O.order_date,OI.prod_id,OI.quantity,OI.item_price
-> FROM Customers AS C,Orders AS O,OrderItems AS OI
-> WHERE C.cust_id = O.cust_id AND OI.order_num = O.order_num AND prod_id = 'RGAN01';
+------------+---------------+---------------------+-----------+------------+----------+--------------+--------------------+-----------------------+-----------+---------------------+---------+----------+------------+
| cust_id | cust_name | cust_address | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email | order_num | order_date | prod_id | quantity | item_price |
+------------+---------------+---------------------+-----------+------------+----------+--------------+--------------------+-----------------------+-----------+---------------------+---------+----------+------------+
| 1000000004 | Fun4All | 829 Riverside Drive | Phoenix | AZ | 88888 | USA | Denise L. Stephens | [email protected] | 20007 | 2012-01-30 00:00:00 | RGAN01 | 50 | 4.49 |
| 1000000005 | The Toy Store | 4545 53rd Street | Chicago | IL | 54545 | USA | Kim Howard | NULL | 20008 | 2012-02-03 00:00:00 | RGAN01 | 5 | 4.99 |
+------------+---------------+---------------------+-----------+------------+----------+--------------+--------------------+-----------------------+-----------+---------------------+---------+----------+------------+
2 rows in set (0.00 sec)
许多联结将一个表中的行与另一个表中的行相关联,单有时候需要包含没有关联行的那些行,例如,可能需要联结完成一下工作:
在上述例子中,联结包含了那些在相关表中没有关联行的行,这种联结被称为外联结。
首先,下面的语句给出一个简单地外联结,检索所有顾客与订单:
mysql> SELECT Customers.cust_id,Orders.order_num
-> FROM Customers INNER JOIN Orders
-> ON Customers.cust_id = Orders.cust_id;
+------------+-----------+
| cust_id | order_num |
+------------+-----------+
| 1000000001 | 20005 |
| 1000000001 | 20009 |
| 1000000003 | 20006 |
| 1000000004 | 20007 |
| 1000000005 | 20008 |
+------------+-----------+
5 rows in set (0.00 sec)
外联结与之的语法是类似的,要检索包括没有订单的顾客在内的所有顾客,可如下进行:
mysql> SELECT Customers.cust_id,Orders.order_num
-> FROM Customers LEFT JOIN Orders
-> ON Customers.cust_id = Orders.cust_id;
+------------+-----------+
| cust_id | order_num |
+------------+-----------+
| 1000000001 | 20005 |
| 1000000001 | 20009 |
| 1000000002 | NULL |
| 1000000003 | 20006 |
| 1000000004 | 20007 |
| 1000000005 | 20008 |
+------------+-----------+
6 rows in set (0.00 sec)
可以看到,这条SELECR语句使用关键字LEFT JOIN,其所指定的行,包括了左侧表(即Customers)的所有行,与右侧表进行联结。在右侧表不存在对应值的时候,值显示为NULL。
RIGHT JOIN类似,指定右侧表的所有行。此外,RIGHT JOIN等同于RIGHT OUTER JOIN,LEFT JOIN等同于LEFT OUTER JOIN。
还存在一种外联结,被称为全外联结,它检索两个表中所有的行,并对那些可以关联的行进行关联。与左外联结或右外联结那些包含一个表的不关联的行不同,全外联结包含两个表的不关联的行。
这种语法使用FULL JOIN,但是要注意的是MySQL不支持FULL JOIN,需要类似的功能的话,要使用UNION或UNION ALL:
mysql> SELECT Customers.cust_id,Orders.order_num
-> FROM Customers LEFT JOIN Orders
-> ON Customers.cust_id = Orders.cust_id
-> UNION ALL
-> SELECT Customers.cust_id,Orders.order_num
-> FROM Customers RIGHT JOIN Orders
-> ON Customers.cust_id = Orders.cust_id;
+------------+-----------+
| cust_id | order_num |
+------------+-----------+
| 1000000001 | 20005 |
| 1000000001 | 20009 |
| 1000000002 | NULL |
| 1000000003 | 20006 |
| 1000000004 | 20007 |
| 1000000005 | 20008 |
| 1000000001 | 20005 |
| 1000000001 | 20009 |
| 1000000003 | 20006 |
| 1000000004 | 20007 |
| 1000000005 | 20008 |
+------------+-----------+
11 rows in set (0.00 sec)
如第9课所述,聚集函数用来汇总数据,虽然至今为止我们举的聚集函数的例子都只是从一个表中汇总数据,单这些函数也可以与联结一起使用。
下例要求检索所有顾客以及每个顾客所下的订单数,使用COUNT()函数完成此工作:
mysql> SELECT Customers.cust_id , COUNT(Orders.order_num) AS num_ord
-> FROM Customers INNER JOIN Orders
-> ON Customers.cust_id = Orders.cust_id
-> GROUP BY Customers.cust_id;
+------------+---------+
| cust_id | num_ord |
+------------+---------+
| 1000000001 | 2 |
| 1000000003 | 1 |
| 1000000004 | 1 |
| 1000000005 | 1 |
+------------+---------+
4 rows in set (0.00 sec)
这条SELECT语句使用INNER JOIN将Customers和Orders互相关联,GROUP BY子句按顾客分组数据,因此,函数调用COUNT(Orders.order_num)对每个顾客的订单计数返回。
聚集函数也可以方便地与其他联结一同使用:
mysql> SELECT Customers.cust_id,COUNT(Orders.order_num) AS num_ord
-> FROM Customers LEFT JOIN Orders
-> ON Customers.cust_id = Orders.cust_id
-> GROUP BY Customers.cust_id;
+————+———+
| cust_id | num_ord |
+————+———+
| 1000000001 | 2 |
| 1000000002 | 0 |
| 1000000003 | 1 |
| 1000000004 | 1 |
| 1000000005 | 1 |
+————+———+
5 rows in set (0.00 sec)
这里需要注意的是,之所以使用COUNT(Orders.order_num)而不是COUNT(),就是因为考虑到这种情况:左联结的情况下,左边的表Customers的所有行都被选中,其中cust_id为1000000002的顾客并没有下订单,对应的Orders表的order_num为空,因此如果COUNT(Orders.order_num)的情况,该顾客的订单数会被归为0,如果使用COUNT()的话,那就是1了。
多数SQL查询,只包含从一个或多个表中返回数据的单条SELECT语句。但是,SQL也允许执行多个查询(多条SELECT语句),并将结果作为一个查询结果集返回。这些组合查询通常称为并(UNION)或复合查询(compound query)。
主要有两种情况,需要使用组合查询:
‘
可以用UNION操作符来组合数条SQL语句,利用UNION可给出多条SELECT语句,将它们的结果组成一个结果集。
使用UNION很简单,所要做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION。
举个例子,假如需要Illinois,Indiana和Michigan等美国几个州所有顾客的报表,还想包括不管位于哪个州的所有Fun4All。WHERE是可以实现的,不过这里使用UNION。
mysql> SELECT cust_name,cust_contact,cust_email FROM Customers WHERE cust_state in ('MI','IL','IN')
-> UNION
-> SELECT cust_name,cust_contact,cust_email FROM Customers WHERE cust_name = 'FUN4ALL';
+---------------+--------------------+-----------------------+
| cust_name | cust_contact | cust_email |
+---------------+--------------------+-----------------------+
| Village Toys | John Smith | [email protected] |
| Fun4All | Jim Jones | [email protected] |
| The Toy Store | Kim Howard | NULL |
| Fun4All | Denise L. Stephens | [email protected] |
+---------------+--------------------+-----------------------+
4 rows in set (0.00 sec)
使用UNION进行组合时,要注意几条规则:
UNION从查询结果集中自动去除了重复的行,如果想返回所有的匹配行,可以使用UNION ALL而非UNION。
具体代码不赘述。
SELECT语句的输出用ORDER BY子句排序,在用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后,对于结果集,不存在用一种方式排序一部分,而用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句。
下面的例子对前面的UNION返回的结果排序:
mysql> SELECT cust_name,cust_contact,cust_email FROM Customers WHERE cust_state in ('MI','IL','IN')
-> UNION
-> SELECT cust_name,cust_contact,cust_email FROM Customers WHERE cust_name = 'FUN4ALL'
-> ORDER BY cust_name,cust_contact;
+---------------+--------------------+-----------------------+
| cust_name | cust_contact | cust_email |
+---------------+--------------------+-----------------------+
| Fun4All | Denise L. Stephens | [email protected] |
| Fun4All | Jim Jones | [email protected] |
| The Toy Store | Kim Howard | NULL |
| Village Toys | John Smith | [email protected] |
+---------------+--------------------+-----------------------+
4 rows in set (0.00 sec)
顾名思义,INSERT语句用于将行插入到数据表,插入有几种方式:
下面逐一介绍这些内容。
把数据插入表中,最简单的方法就是使用基本的INSERT语法,它要求指定表名和插入到新行中的值。
下面举一个例子:
mysql> INSERT INTO Customers
-> VALUES(
-> '1000000006',
-> 'Toy Land',
-> '123 Any Street',
-> 'New York',
-> 'NY',
-> '11111',
-> 'USA',
-> NULL,
-> NULL);
Query OK, 1 row affected (0.01 sec)
这样,就将一个新顾客插入到了Customers表:
mysql> SELECT * FROM CUstomers;
+------------+---------------+----------------------+-----------+------------+----------+--------------+--------------------+-----------------------+
| cust_id | cust_name | cust_address | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email |
+------------+---------------+----------------------+-----------+------------+----------+--------------+--------------------+-----------------------+
| 1000000001 | Village Toys | 200 Maple Lane | Detroit | MI | 44444 | USA | John Smith | [email protected] |
| 1000000002 | Kids Place | 333 South Lake Drive | Columbus | OH | 43333 | USA | Michelle Green | NULL |
| 1000000003 | Fun4All | 1 Sunny Place | Muncie | IN | 42222 | USA | Jim Jones | [email protected] |
| 1000000004 | Fun4All | 829 Riverside Drive | Phoenix | AZ | 88888 | USA | Denise L. Stephens | [email protected] |
| 1000000005 | The Toy Store | 4545 53rd Street | Chicago | IL | 54545 | USA | Kim Howard | NULL |
| 1000000006 | Toy Land | 123 Any Street | New York | NY | 11111 | USA | NULL | NULL |
+------------+---------------+----------------------+-----------+------------+----------+--------------+--------------------+-----------------------+
6 rows in set (0.00 sec)
这样的语法很简单,但并不安全,上面的SQL语句高度依赖于表中列的顶一次需,还依赖于其容易获得的次序信息。即使可以得到这种次序信息,也不能保证各列在下一次表结构变动后保持完全相同的次序。
更安全也繁琐的方法如下:
mysql> INSERT INTO Customers(
-> cust_id,
-> cust_name,
-> cust_address,
-> cust_city,
-> cust_state,
-> cust_zip,
-> cust_country,
-> cust_contact,
-> cust_email)
-> VALUES(
-> '1000000006',
-> 'Toy Land',
-> '123 Any Street',
-> 'New York',
-> 'NY',
-> '11111',
-> 'USA',
-> NULL,
-> NULL);
ERROR 1062 (23000): Duplicate entry '1000000006' for key 'PRIMARY'
由于主键重复输入失败,不过形式是这样的。
后者可以使用与原表不同的次序插入。
使用INSERT的推荐方法是明确给出表的列名,使用这种语法也可以省略部分列。
这表示可以只给某些列提供值,而其他的列不提供值。
语句例子省略。
INSERT还存在另一种形式,可以利用它将SELECT语句的结果插入表中,这就是所谓的INSERT SELECT,顾名思义,是由一条INSERT语句和一条SELECT语句组成的。这里,SELECT语句取代的是VALUES的位置。INSERT通常只插入一行,要插入多行,必须执行多个INSERT语句,不过INSERT SELECT是例外。
有一种数据插入不使用INSERT语句,要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用SELECT INTO语句。与INSERT SELECT是将数据添加到一个已经存在的表里不同,SELECT INTO将数据复制到一个新表中。
INSERT SELECT和SELECT INTO的一个重要差别是,前者导出数据,后者导入数据。
下面的例子说明如何使用SELECT INTO:
mysql> SELECT *
-> INTO CustCopy
-> FROM Customers;
ERROR 1327 (42000): Undeclared variable: CustCopy
要注意的是,MySQL中不支持这样直接创建新表。
要手动建表,然后复制进去才行。