一列或者一组列,其值能够唯一标识表中的每一行。
主键满足以下条件:
本文所使用的的数据库为mysql
创建一个订单系统数据库
CREATE DATABASE IF NOT EXISTS order_sys default character set utf8 COLLATE utf8_general_ci;
创建商品表
CREATE TABLE Products
(
prod_id char(10) NOT NULL ,
vend_id char(10) NOT NULL ,
prod_name char(255) NOT NULL ,
prod_price decimal(8,2) NOT NULL ,
prod_desc text NULL
);
创建顾客表
CREATE TABLE Customers
(
cust_id char(10) NOT NULL ,
cust_name char(50) NOT NULL ,
cust_address char(50) NULL ,
cust_city char(50) NULL ,
cust_state char(5) NULL ,
cust_zip char(10) NULL ,
cust_country char(50) NULL ,
cust_contact char(50) NULL ,
cust_email char(255) NULL
);
创建供应商表
CREATE TABLE Vendors
(
vend_id char(10) NOT NULL ,
vend_name char(50) NOT NULL ,
vend_address char(50) NULL ,
vend_city char(50) NULL ,
vend_state char(5) NULL ,
vend_zip char(10) NULL ,
vend_country char(50) NULL
);
创建订单表
CREATE TABLE Orders
(
order_num int NOT NULL ,
order_date datetime NOT NULL DEFAULT CURRENT_DATE(),
cust_id char(10) NOT NULL
);
创建每个订单实际商品表
CREATE TABLE OrderItems
(
order_num int NOT NULL ,
order_item int NOT NULL ,
prod_id char(10) NOT NULL ,
quantity int NOT NULL DEFAULT 1,
item_price decimal(8,2) NOT NULL
);
设置主键
ALTER TABLE Customers ADD PRIMARY KEY (cust_id);
ALTER TABLE OrderItems ADD PRIMARY KEY (order_num, order_item);
ALTER TABLE Orders ADD PRIMARY KEY (order_num);
ALTER TABLE Products ADD PRIMARY KEY (prod_id);
ALTER TABLE Vendors ADD PRIMARY KEY (vend_id);
设置外键
ALTER TABLE OrderItems ADD CONSTRAINT FK_OrderItems_Orders FOREIGN KEY (order_num) REFERENCES Orders (order_num);
ALTER TABLE OrderItems ADD CONSTRAINT FK_OrderItems_Products FOREIGN KEY (prod_id) REFERENCES Products (prod_id);
ALTER TABLE Orders ADD CONSTRAINT FK_Orders_Customers FOREIGN KEY (cust_id) REFERENCES Customers (cust_id);
ALTER TABLE Products ADD CONSTRAINT FK_Products_Vendors FOREIGN KEY (vend_id) REFERENCES Vendors (vend_id);
添加新的列
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
删除列
ALTER TABLE Vendors
DROP COLUMN vend_phone;
DROP TABLE Vendors;
返回过滤掉重复的数据
SELECT DISTINCT vend_id FROM Products;
Oracle:ROWNUM
SELECT prod_name FROM Products ROWNUM <=5;
Mysql、MariaDB、PostgreSQL、SQLite
SELECT prod_name FROM Products LIMIT 5;
SELECT prod_name FROM Products LIMIT 5 OFFSET 3;
#LIMIT 5 OFFSET 3 表示查询从第3行起 的5条数据
#LIMIT指定返回的行数,OFFSET 指定从哪里开始
#Mysql MariaDB也可以写成如下:
SELECT prod_name FROM Products LIMIT 3,5; #即 表示从第3行起,查询5条数据
行内注释
SELECT prod_name -- 注释
FROM Products;
整行注释
# 整行注释
SELECT prod_name
FROM Products;
多行注释
/*SELECT prod_name
FROM Products; */
SELECT prod_name
FROM Products;
单列排序
SELECT prod_name
FROM Products
ORDER BY prod_name;
ORDER BY 需要保证它是SELECT 语句中最后一条字句
多列排序
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price, prod_name;
首先先按照prod_price 排序,仅有当多行具有相同的prod_price时,才根据prod_name排序。
按列位置排序
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY 2, 3;
ORDER BY 后指定的是SELECT清单中的相对位置,即2代表上面查询的字段prod_price,3代表prod_name,排序的先后顺序为先prod_price,仅有当多行具有相同的prod_price时,才根据prod_name排序。
默认是升序排序,关键字ASC(ASCENDING)
降序排序 使用关键字DESC (DESCENDING )
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC, prod_name;
首先按照 prod_price 进行降序排序,当有多行具有相同的 prod_price 时 对 prod_name 进行升序排序。即如果要对多列进行降序排序,那么需要在每个列名后都添加DESC关键字。
DESC只应用到直接位于其前面的列
SELECT prod_name, prod_price
FROM Products
WHERE prod_price = xxx;
表示从 Products 表中查询 prod_price 为xxx的数据。
同时使用ORDER BY 和 WHERE 子句时,应该让 ORDER BY 位于 WHERE 之后。
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
!< | 不小于(mysql等不支持) |
> | 大于 |
>= | 大于等于 |
!> | 不大于(mysql等不支持) |
BETWEEN | 在指定的两值之间(闭区间) |
为NULL值 |
Tip: NULL 值为无值,它与字段包含0、空字符串或仅仅包含空格不同
AND 操作符可以为子句添加条件
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
这条语句是查询 vend_id 为 ‘DLL01’ 并且 prod_price 小于等于4的产品,AND 需要 DBMS 返回满足所有条件的子句的结果。
OR 操作符指示 DBMS 检索匹配任一条件的行。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
这条语句是查询 vend_id 为 ‘DLL01’ 或者 'BRS01’的产品。
在SQL中AND 操作符的优先级要高于 OR。所以在使用AND和OR时 要尽量的加入圆括号,以避免产生歧义。
IN 操作符用来指定条件范围
SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ('DLL01', 'BRS01')
此语句查询了 vend_id 为 DLL01 和 BRS01的所有商品。它在子句中的功能和 OR 相当。
优点:
NOT 需要与其他操作符配合使用,用来否定其后所跟的条件。
SELECT prod_name
FROM Products
WHERE NOT vend_id = 'DLL01'
此语句查询了除了 vend_id 为’ DLL01 '的其他所有商品。
NOT 还可以与 IN 共同使用
SELECT prod_name, prod_price
FROM Products
WHERE NOT vend_id IN ('DLL01', 'BRS01');
在这里 NOT 操作符也可以加在 IN 之前。
通配符:用来匹配值的一部分的特殊字符。
通配符搜索只能用于文本字段(字符串),非文本数据类型不能使用通配符。
在搜索子句中使用通配符必须使用 LIKE 操作符,意在指示 DBMS 后跟的搜索模式利用通配符而不是简单的相等匹配。
在搜索串中 % 表示任何字符出现任意次数(包括0个)。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
此语句是找到所有以词 Fish 开头的产品。
通配符 % 无法匹配 NULL。
下划线表示只匹配单个字符
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '_ish bean bag toy'
下划线 _ 总是刚好匹配一个字符,不能多也不能少。
Access和 SQL Serve 使用 + 号
DB2、Oracle、PostgreSQL、SQLite使用 ||
Mysql 和 MariaDB 使用 Concat() 函数
比如查询供应商表中的 vend_name 和 vend_country , 并将国家与名字进行拼接
SELECT Concat(vend_name,'(',vend_country,')')
FROM Vendors
ORDER BY vend_name;
其结果如下:
+----------------------------------------+
| Concat(vend_name,'(',vend_country,')') |
+----------------------------------------+
| Bear Emporium(USA) |
| Bears R Us(USA) |
| Doll House Inc.(USA) |
| Fun and Games(England) |
| Furball Inc.(USA) |
| Jouets et ours(France) |
+----------------------------------------+
通过结果可以看到 Concat 函数拼接了4个参数,一个是vend_name ,一个左括号,一个 vend_country ,一个右括号。
上面的结果头部字段名很长 没有一个字段表示,那么我们可以为其添加一个别名(AS)。
SELECT Concat(vend_name,'(', vend_country,')')
AS vend_title
FROM Vendors
ORDER BY vend_name;
结果如下:
+------------------------+
| vend_title |
+------------------------+
| Bear Emporium(USA) |
| Bears R Us(USA) |
| Doll House Inc.(USA) |
| Fun and Games(England) |
| Furball Inc.(USA) |
| Jouets et ours(France) |
+------------------------+
对检索出的数据进行算数运算。
比如检索订单中的数量和单价,那么可以通过数量和单价计算出商品的总价。
SELECT prod_id,
quantity,
item_price,
quantity * item_price AS totle_price
FROM OrderItems WHERE order_num = 20008;
结果如下:
+---------+----------+------------+-------------+
| prod_id | quantity | item_price | totle_price |
+---------+----------+------------+-------------+
| RGAN01 | 5 | 4.99 | 24.95 |
| BR03 | 5 | 11.99 | 59.95 |
| BNBG01 | 10 | 3.49 | 34.90 |
| BNBG02 | 10 | 3.49 | 34.90 |
| BNBG03 | 10 | 3.49 | 34.90 |
+---------+----------+------------+-------------+
函数 | 说明 |
---|---|
LEFT() | 返回字符串左边的字符 |
RIGHT() | 返回字符串右边的字符 |
LENGTH() | 返回字符串长度 |
LOWER() | 将字符串转换成小写 |
UPPER() | 将字符串转换成大写 |
LTRIM() | 去掉字符串左边的空格 |
RTRIM() | 去掉字符串右边的空格 |
SELECT order_num
FROM Orders
WHERE EXTRACT(YEAR FROM order_date) = 2012;
此语句查询 order_date 年份为2012年的订单号。
EXTRACT() 可以提取出年、月、日、小时等。
比如将 Oders 表中的日期按照年月日检索出来
SELECT EXTRACT(YEAR FROM order_date) AS order_year,
EXTRACT(MONTH FROM order_date) AS order_month,
EXTRACT(DAY FROM order_date) AS order_day
FROM Orders;
结果如下:
+------------+-------------+-----------+
| order_year | order_month | order_day |
+------------+-------------+-----------+
| 2012 | 5 | 1 |
| 2012 | 1 | 12 |
| 2012 | 1 | 30 |
| 2012 | 2 | 3 |
| 2012 | 2 | 8 |
+------------+-------------+-----------+
除了 EXTRACT 函数 mysql 还可以使用YEAR()、MONTH()、DAY() 提取年月日。
DATE_ADD() 可以向日期添加指定的时间间隔
SELECT DATE_ADD(order_date, INTERVAL 10 DAY)
AS inv_date
FROM Orders
WHERE order_date='2012-05-01 00:00:00';
返回如下:
+---------------------+
| inv_date |
+---------------------+
| 2012-05-11 00:00:00 |
+---------------------+
DATE_FORMAT() 用来格式化日期
SELECT DATE_FORMAT(order_date,'%m-%d-%Y %H:%i:%s') AS format_date FROM Orders;
返回如下:
+---------------------+
| format_date |
+---------------------+
| 05-01-2012 00:00:00 |
| 01-12-2012 00:00:00 |
| 01-30-2012 00:00:00 |
| 02-03-2012 00:00:00 |
| 02-08-2012 00:00:00 |
+---------------------+
具体参数详见
函数 | 说明 |
---|---|
ABS() | 绝对值 |
COS() | 返回一个角度的余弦 |
SIN() | 返回一个角度的正弦 |
TAN() | 返回一个角度的正切 |
EXP() | 返回一个数的指数值 |
PI() | 返回圆周率 |
SQRT() | 返回一个数的平方根 |
函数 | 说明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值的和 |
比如需要返回 Products 中的所有商品中的平均价钱
SELECT AVG(prod_price) AS avg_price
FROM Products;
返回:
+-----------+
| avg_price |
+-----------+
| 6.823333 |
+-----------+
AVG函数忽略值为NULL的列
下面先让我们看一下 Customers 表中 email 的数据,其中有2个 NULL 值
SELECT cust_email FROM Customers;
+-----------------------+
| cust_email |
+-----------------------+
| sales@villagetoys.com |
| NULL |
| jjones@fun4all.com |
| dstephens@fun4all.com |
| NULL |
+-----------------------+
如果我们使用 COUNT( * )
SELECT COUNT(*)
FROM Customers;
+----------+
| COUNT(*) |
+----------+
| 5 |
+----------+
改为使用 COUNT( column )
SELECT COUNT(cust_email)
FROM Customers;
+-------------------+
| COUNT(cust_email) |
+-------------------+
| 3 |
+-------------------+
MAX() 函数一般用于找出最大的数值或者日期
对于数值函数使用,返回指定列的最大的数
SELECT MAX(prod_price) AS max_price
FROM Products;
结果
+-----------+
| max_price |
+-----------+
| 11.99 |
+-----------+
对于非数值函数使用,用于文本数据时,MAX() 返回按该列排序后的最后一行。
MAX() 函数忽略列值为 NULL 的行
SUM() 用来返回指定列值的和。
SELECT SUM(quantity) AS items_ordered
FROM OrderItems;
此语句返回订单中所有物品数量的总和。
也可用于执行算数操作符
SELECT
SUM(quantity* item_price)
AS totle_price
FROM OrderItems;
SUM()函数忽略列值为 NULL 的行。
对于聚集函数都可以使用 ALL 或 DISTICT
比如使用 AVG() 返回所有产品的平均价格,使用 DISTINCT 参数,平均值只计算各个不同的价格
SELECT
AVG( DISTINCT prod_price)
AS avg_price
FROM Products;
返回结果:
+-----------+
| avg_price |
+-----------+
| 7.490000 |
+-----------+
DISTINCT 不能用于COUNT( * ),如果指定列名,则 DISTINCT 只能用于COUNT()。
SELECT 语句可以根据需要包含多个聚集函数。
SELECT COUNT(*) AS num_items,
MIN(prod_price) AS price_min,
MAX(prod_price) AS price_max,
AVG(prod_price) AS price_avg
FROM Products;
返回结果:
+-----------+-----------+-----------+-----------+
| num_items | price_min | price_max | price_avg |
+-----------+-----------+-----------+-----------+
| 9 | 3.49 | 11.99 | 6.823333 |
+-----------+-----------+-----------+-----------+
聚合函数用来汇总数据,使用聚合函数会很高效。
GROUP BY 根据一个或者多个列对结果集进行分组。
比如我们想要查询某个产品中每个供应商的个数
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
返回结果:
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01 | 3 |
| DLL01 | 4 |
| FNG01 | 2 |
+---------+-----------+
此语句中 COUNT( * ) 为计算字段,GROUP BY 表示根据 vend_id 排序并分组,所以COUNT( * ) 计算的是每个分组的个数。
GROUP BY 可以指定多个列:
SELECT vend_id, COUNT(*) AS num_prods , prod_price
FROM Products
GROUP BY vend_id, prod_price;
+---------+-----------+------------+
| vend_id | num_prods | prod_price |
+---------+-----------+------------+
| BRS01 | 1 | 5.99 |
| BRS01 | 1 | 8.99 |
| BRS01 | 1 | 11.99 |
| DLL01 | 3 | 3.49 |
| DLL01 | 1 | 4.99 |
| FNG01 | 2 | 9.49 |
+---------+-----------+------------+
此sql语句表示每个供应商的每个商品价钱的分组。
通过结果我们发现 GROUP BY X, Y 应用多列时,就是将具有相同 X 和 Y 放到一个分组中。
使用技巧:
GROUP BY 子句可以包含任意数目的列
GROUP BY 子句中嵌套了分组时,数据将在最后指定的分组上进行汇总(如上面例子所示,COUNT( * ) 是针对 prod_price 进行的汇总)。
GROUP BY 中的列名必须是索引列或是有效的表达式,即如果在SELECT 中使用表达式,那么必须在 GROUP BY 子句中使用相同的表达式,不可以使用别名。
除了聚合计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句中给出。
如果分组列中包含具有 NULL 值的行,那么 NULL 将作为一个分组返回。
GROUP BY 子句必须出现在 WHERE 子句后,ORDER BY 子句前。
可以通过相对位置指定列,例如 GROUP BY 2, 1表示先按照第二个列分组,然后再按照第一个列分组。
HAVING 用于过滤分组
WHERE 用于过滤行
SELECT cust_id,COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
返回结果
+------------+--------+
| cust_id | orders |
+------------+--------+
| 1000000001 | 2 |
+------------+--------+
此sql语句返回了两个以上订单的分组。
那么可不可以将 WHERE 和 HAVING 一起使用?
例如我们想列出具有两个以上产品并且其价格大于 4 的供应商
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;
返回结果:
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01 | 3 |
| FNG01 | 2 |
+---------+-----------+
可见,WHERE 是数据分组前进行的过滤,HAVING 在数据分组后进行过滤。
如果不指定 GROUP BY 那么大多数的 DBMS 会将 WHERE 和 HAVING 同等对待。
GROUP BY | ORDER BY |
---|---|
对行分组,但输出可能不是分组的顺序 | 对产生的输出排序 |
只可能使用选择列或表达式列,而且必须使用每个选择列表达式 | 任意列都可以使用,甚至非选择列也可以 |
如果与聚集函数一起使用列,则必须使用 | 不一定需要 |
检索包含三个或更多物品的订单号和订购物品的数目
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3;
+-----------+-------+
| order_num | items |
+-----------+-------+
| 20006 | 3 |
| 20007 | 5 |
| 20008 | 5 |
| 20009 | 3 |
+-----------+-------+
如果我们希望订购的物品数按照从大到小输出怎么办
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >=3
ORDER BY items DESC, order_num DESC;
返回结果
+-----------+-------+
| order_num | items |
+-----------+-------+
| 20008 | 5 |
| 20007 | 5 |
| 20009 | 3 |
| 20006 | 3 |
+-----------+-------+
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组 | 仅在按组计算聚合时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序 | 否 |
子查询:嵌套在其他查询中的查询。
本文中 Orders 表中存储的是订单id、日期、顾客id
OrderItems 表中存储的是订单id、商品id、订单价钱、订单数量等信息
Customers 中存储顾客信息
如果想要列出订购物品 RGAN01 的所有顾客,我们需要进行以下步骤:
在 SELECT 语句中,子查询总是从内向外处理。
SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN(
SELECT cust_id
FROM Orders
WHERE order_num IN (
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01'));
返回结果
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
作为子查询的 SELECT 语句只能查询单个列。
如果需要显示 Customers 表中,每个顾客的订单数,订单与相对应的顾客ID 存储在Orders表中。
需要进行以下步骤:
SELECT
cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id)
AS orders
FROM Customers
ORDER BY cust_name;
返回结果
+---------------+------------+--------+
| cust_name | cust_state | orders |
+---------------+------------+--------+
| Fun4All | IN | 1 |
| Fun4All | AZ | 1 |
| Kids Place | OH | 0 |
| The Toy Store | IL | 1 |
| Village Toys | MI | 2 |
+---------------+------------+--------+
此 sql 语句返回了每个顾客的 cust_name、cust_state、orders 信息。其中 orders 是一个计算字段,通过子查询所建立,该子查询对检索出的顾客执行一次,在此例中,该子查询执行了5次,因为检索出了5个顾客。
子查询常用在 WHERE 子句的IN操作符中,以及用来填充计算列。
关系表:关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联。
在我们的例子总 Vendors 是供应商表,每个供应商信息是一行。
Products 表只存储产品信息,一列存储供应商ID。
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
返回结果
+-----------------+---------------------+------------+
| vend_name | prod_name | prod_price |
+-----------------+---------------------+------------+
| Bears R Us | 8 inch teddy bear | 5.99 |
| Bears R Us | 12 inch teddy bear | 8.99 |
| Bears R Us | 18 inch teddy bear | 11.99 |
| Doll House Inc. | Fish bean bag toy | 3.49 |
| Doll House Inc. | Bird bean bag toy | 3.49 |
| Doll House Inc. | Rabbit bean bag toy | 3.49 |
| Doll House Inc. | Raggedy Ann | 4.99 |
| Fun and Games | King doll | 9.49 |
| Fun and Games | Queen doll | 9.49 |
+-----------------+---------------------+------------+
此 sql 语句使用 WHERE 子句将两个表正确的联结,这里是联结条件。
在引用的列可能出现歧义时,必须使用完全限定列名(即表名.列名)。
在联结2个表时实际要做的就是将第一表中的每一行与第二个表中的每一行配对,如果没有 WHERE 子句,那么两个表将做笛卡尔积。
以上所演示的我们称之为等值联结,它基于两个表之间的相等测试。这种联结也称为内联结。
下面我们使用另一种方式实现内联结
SELECT vend_name, prod_name, prod_price
FROM Vendors
INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
返回结果
+-----------------+---------------------+------------+
| vend_name | prod_name | prod_price |
+-----------------+---------------------+------------+
| Bears R Us | 8 inch teddy bear | 5.99 |
| Bears R Us | 12 inch teddy bear | 8.99 |
| Bears R Us | 18 inch teddy bear | 11.99 |
| Doll House Inc. | Fish bean bag toy | 3.49 |
| Doll House Inc. | Bird bean bag toy | 3.49 |
| Doll House Inc. | Rabbit bean bag toy | 3.49 |
| Doll House Inc. | Raggedy Ann | 4.99 |
| Fun and Games | King doll | 9.49 |
| Fun and Games | Queen doll | 9.49 |
+-----------------+---------------------+------------+
SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
返回结果
+---------------------+-----------------+------------+----------+
| prod_name | vend_name | prod_price | quantity |
+---------------------+-----------------+------------+----------+
| 18 inch teddy bear | Bears R Us | 11.99 | 50 |
| Fish bean bag toy | Doll House Inc. | 3.49 | 100 |
| Bird bean bag toy | Doll House Inc. | 3.49 | 100 |
| Rabbit bean bag toy | Doll House Inc. | 3.49 | 100 |
| Raggedy Ann | Doll House Inc. | 4.99 | 50 |
+---------------------+-----------------+------------+----------+
此 sql 语句显示订单20007中的物品。
如果我们实现上面子查询中的“如果想要列出订购物品 RGAN01 的所有顾客”这个例子,可以写成这样
SELECT cust_name, cust_contact
FROM OrderItems, Orders, Customers
WHERE Orders.cust_id= Customers.cust_id
AND OrderItems.order_num = Orders.order_num
AND OrderItems.prod_id='RGAN01';
返回结果
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
接下来再看下 INNER JOIN ON 实现方式
SELECT
cust_name,
cust_contact
FROM (Customers
INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id)
INNER JOIN OrderItems
ON OrderItems.order_num = Orders.order_num
WHERE OrderItems.prod_id = 'RGAN01';
返回结果
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
假如要给与 Jim Jones 同公司的所有顾客发送一封邮件。这个要求包括下面两步:
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 |
+------------+-----------+--------------------+
使用自联结
SELECT C1.cust_id, C1.cust_name, C1.cust_contact
FROM Customers AS C1
INNER JOIN Customers AS C2
ON C1.cust_name = C2.cust_name
WHERE C2.cust_contact = 'Jim Jones';
+------------+-----------+--------------------+
| cust_id | cust_name | cust_contact |
+------------+-----------+--------------------+
| 1000000003 | Fun4All | Jim Jones |
| 1000000004 | Fun4All | Denise L. Stephens |
+------------+-----------+--------------------+
通过以上 sql 语句发现 ,如果引用2个相同的表必须指定别名,如果没有别名会报错,因为 DBMS 无法区分你要查找的字段。
自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符(SELECT * ),而对其他表的列使用明确的子集来完成。
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 OI.prod_id = 'RGAN01';
联结包含相关表中没有关联行的行,这种联结称为外联结。
SELECT Customers.cust_id, Orders.order_num
FROM Customers
LEFT OUTER 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 |
+------------+-----------+
使用外联结时 必须指定 RIGHT 或 LEFT 关键字指定其所有行的表( RIGHT 指出的是 OUTER JOIN 右边的表,LEFT 指出的是 OUTER JOIN 右边的表)。
上面例子中 LEFT OUTER JOIN 从 FROM 子句左边的表( Customers )中选择所有行。
左外联结:左表中所有不符合条件的数据行,并在其中的右表列填写NULL,即 上面的例子中 cust_id = 1000000002 是不符合条件的数据行,那么添加到 Orders 表中是 NULL 。
那么来看一个右外联结
SELECT Customers.cust_id, Orders.order_num
FROM Customers
RIGHT OUTER JOIN Orders
ON Orders.cust_id = Customers.cust_id;
+------------+-----------+
| cust_id | order_num |
+------------+-----------+
| 1000000001 | 20005 |
| 1000000001 | 20009 |
| 1000000003 | 20006 |
| 1000000004 | 20007 |
| 1000000005 | 20008 |
+------------+-----------+
发现其中缺少了 cust_id 为 1000000002 的行,因为右外联结是从 Orders 表中选择所有行,将其添加到左表中,因为 Orders 表中没有 cust_id 为 1000000002 的行,也就不存在和左表不匹配的问题了。
右外联结:右表中所有不符合条件的数据行,并在其中的左表列填写NULL
要检索所有顾客及每个顾客所下的订单数
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 |
+------------+---------+
同样的使用外联结的方式
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT OUTER 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 |
+------------+---------+
这个例子使用了左外联结来包含所有顾客,甚至包含了那些没有任何订单的顾客,因此 1000000002 也出现在了结果中。
利用 UNION 操作符将多个 SELECT 语句组合成一个结果集。
使用场景:
SELECT cust_name, cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name='Fun4All';
UNION 使用规则: