第10课 分组数据
数据分组
- 返回每个供应商提供的产品数目
select vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
GROUP BY 子句对每个组而不是整个结果进行聚集。
过滤分组
having和where的区别:WHERE过滤指定的是行而不是分组。WHERE过滤行,而HAVING过滤分组。
过滤分组,规定包括哪些分组,排除哪些分组。
- 列出至少有两个订单的所有顾客(必须基于完整的分组而不是个别的行进行过滤)
SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >=2;
WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。
- 检索具有两个以上产品且其价格大于等于4的供应商
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prob_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;
where子句过滤所有prod_price至少为4的行,然后按vend_id分组数据,having子句过滤计数为2或2以上的分组。
- 检索包含三个或更多物品的订单号和订购物品的数目,按订购物品的数目排序输出
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;
//输出
order_num items
--------- ------
200006 3
200009 3
200007 5
200008 5
使用group by子句按照订单号(order_num列)分组数据,以便count(*)函数能够返回每个订单中的物品数目。having子句过滤数据,使得只返回包含三个或更多物品的订单。
第11课 利用子查询进行过滤
1、Vendors表包括供应商名称和地址信息;
2、Orders表包含收到的所有订单;
3、OrderItems表包含每个订单中的各项物品。
订单存储在两个表中,每个订单包括订单编号,客户iD,订单日期,在Orders表中存储为一行。
各订单的物品存储在相关的OrderItems表中。Orders表不存储顾客信息,只存储客户ID。顾客的实际信息存储在Customers表中。
- 列出订购物品RGAN01的所有顾客
(1)检索包含物品RGAN01的所有订单的编号。
(2)检索具有前一步骤列出的订单编号的所有顾客的ID。
(3)检索前一步骤返回的所有谷歌ID的顾客信息。
//(1)
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGANO01'
order_num
----------
20007
20008
//(2)
SELECT cust_id
FROM Orders
WHERE order_num IN (200007,20008);
cust_id
-------
100000004
100000005
//(3)
SELECT cust_name, cust_concact
FROM Customers
WHERE cust_id IN ('100000004','100000005');
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 = 'GRAN01'
));
- 需要显示Customers表中每个顾客的订单总数。 订单与相应的顾客ID存储在Orders表中。
(1)从Customers表中检索顾客列表;
(2)对于检索出的每个顾客,统计其在Orders表中的订单数目。
如果对顾客100000001的订单进行计数
SELECT COUNT(*) AS orders
FROM Orders
WHERE cust_id = ’1000000001‘;
要对每一个顾客执行count(*),则应该作为子查询。
SELECT cust_name,cust_state,
(SELECT COUNT(*) FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
该子查询对检索出的每个顾客执行一次。
第12课 联结表
相同的数据出现多次不是一件好事。关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值相互关联(所以才叫关系数据库)。
如果数据存储在多个表中,怎么样用一条select语句就检索出数据呢?使用结联。
SELECT vend_name,prod_name,prod_price
FROM Vendors,Products
WHERE Vendors.vend_id = Products.vend_id;
在联结两个表时,实际要做的是将第一表中的每一行与第二个表中的每一行配对。WHERE子句作为过滤条件,只包含那些匹配给定条件的行。
由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
内联结
基于两个表之间的相等测试。
与上面相同
SELECT vend_name,prod_name,prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
联结多个表
- 显示订单20007中的物品
SELECT prod_name,vend_name,prod_name,prod_price,quantity
FROM OrderItems,Vendors,Products
WHERE Vendors.vend_id = Products.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
所以子查询可以用内联结代替
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 = 'GRAN01'));
SELECT cust_name,cust_contact
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'GRAN01'));
第13课 创建高级联结
使用表别名
可以给表名起别名,列名和计算字段也可以。
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 = 'GRAN01'));
使用不同类型的联结
除了内联结,还有自联结,自然联结,外联结。
自联结
使用表别名的主要原因是能在一条select语句中不止一次引用相同的表。
- 要给jim同一公司的所有顾客发送一封信件。
(1)找到jim工作的公司;
(2)找到在该公司工作的顾客。
SELECT cust_id,cust_name,cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
FROM Customers
WHERE cust_concact = 'jim');
Customers表在from子句中出现了两次,使用自联结如下:
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1,Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_concact = 'jim');
自联结通常用来代替从相同表中检索数据的使用子查询语句。
自然联结
无论何时对表进行联结,应该至少有一列不止出现在一个表中。(就是被联结的那个列肯定是在两个表里都出现啦)。内联结返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使得每一列只返回一次。
自然联结要求只能选择那些唯一的列,一般通过对一个表使用通配符(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 prod_id = 'GRAN01'));
通配符只对第一表使用,所有其他列明确列出,所以,没有重复的列被检索出来。
(不太理解。C.cust_id = O.cust_id, OI.order_num = O.order_num重复)
外联结
以上联结都是将一个表中的行与另一个表中的行相关联,但是有时候需要包含没有关联行的那些行。(我的理解是两个表里没有重复的列)
对每个顾客下的订单进行计数,包括那些至今尚未下订单的顾客;
列出所有产品以及订购数量,包括没有人订购的产品;
外联结:那些在相关表中没有关联行的行。
- 检索所有顾客及其订单(内联结)
SELECT Customers.cust_id, Orders.order_num
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
-
检索包括没有订单顾客在内的所有顾客(外联结)
也就是说cust_id这个顾客在Orders表中是没有数据的。
SELECT Customers.cust_id, Orders.order_num
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
cust_id order_num
------- ----------
1000001 20005
1000001 20009
1000002 NULL
1000003 20006
1000004 20007
1000005 20008
(right指的是outer join右边的表,left是outer join左边的表)。上面的例子是从Customers表中选择所有行。
全外联结检索两个表中的所有行并关联那些可以关联的行。即包含两个表的不关联的行。
SELECT Customers.cust_id, Orders.order_num
FROM Customers FULL OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
- 检索所有顾客和每个顾客所下的订单数
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers INNNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
cust_id order_num
------- ----------
1000001 2
1000003 1
1000004 1
1000005 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 order_num
------- ----------
1000001 2
1000002 0
1000003 1
1000004 1
1000005 1
第14课 组合查询
- 需要A,B,C等几个美国州的所有顾客的报表,还行包括不管位于哪个州的所有的fun4all。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('A','B','C')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'full4all';
多条件的where子句
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('A','B','C')
OR cust_name = 'full4all';
使用UNION时,重复的行会被自动取消。如果想返回所有的匹配行,使用NUION ALL。
NULL值与空字符串不同。
NULL值是没有值,不是空字符串。
‘’空字符换是一个有效的值,它不是无值。