MySQL知识点整理已经进行到第四期了,间隔一段时间再看以前的题目,蓦然发现有些操作莫名其妙,可能是对表的操作还是没有透彻的理解。暑假可能要去实习了,做的就是SQL,希望归来是王者~
之前我们整理过聚集函数,总共有5个。我们使用聚集函数可以对行进行计数,计算平均值,求和,最大值和最小值。目前为止,所有的计算都是在表的所有数据或匹配特定的WHERE子句的数据上进行的。
比如,下面的例子返回的是供应商1003提供的产品数目:
SELECT COUNT(*) AS num_prods
FROM products
WHERE vend_id = '1003';
结果:
若要返回每个供应商提供的产品数目呢?提供10个以上的产品的供应商呢?
这里,我们将使用分组,将数据分为多个逻辑组,以便对每个分组进行聚集计算。
比如,我们返回每个供应商提供的产品数目,
SELECT vend_id,COUNT(*) AS num_prods
FROM products
GROUP BY vend_id;
结果:
GROUP BY 按vend_id排序对每个供应商进行分组,然后对每个分组,分别计算num_prods,即提供的产品的个数。
使用GROUP BY 子句的一些规定:
在进行分组以后,我们需要排除一些分组,比如,列出至少有两个以上订单的顾客。为了得到这种数据,必须基于完整的分组而不是个别的行进行过滤,即where子句不能使用。而HAVING子句用来过滤分组。
比如,返回订单数不小于2的顾客,
SELECT cust_id,COUNT(*) AS orders
FROM orders
GROUP BY cust_id
HAVING COUNT(*) >=2;
结果:
我们可以在一条语句中同时使用WHERE和HAVING子句,比如,要返回具有2个或2个以上、价格不小于10的产品的供应商。
SELECT vend_id,COUNT(*) AS num_prods
FROM products
WHERE prod_price >=10
GROUP BY vend_id
HAVING COUNT(*) >=2;
为了说明GROUP BY 和ORDER BY 语句的用法,我们举个栗子:检索总计订单价格不小于50的订单的订单号和总计订单价格:
我们先看只有GROUP BY 的情况:
SELECT order_num,SUM(quantity*item_price) AS ordertotal
FROM orderitems
GROUP BY order_num
HAVING SUM(quantity*item_price) >=50;
结果:
我们再看GROUP BY和HAVING同时存在的情况:
SELECT order_num,SUM(quantity*item_price) AS ordertotal
FROM orderitems
GROUP BY order_num
HAVING SUM(quantity*item_price) >=50
ORDER BY ordertotal;
结果:
我们可以看到order_num没有变化,但是ordertotal按照从小到大的顺序进行了排序。一般而言,ORDER BY 子句默认是升序排列,如果想逆序排列,则在后面加 DESC即可。
书写顺序:
s e l e c t → f r o m → w h e r e → g r o u p b y → h a v i n g → o r d e r b y → l i m i t select \ \to from\ \to where\ \to group\ by\ \to having\ \to order \ by\ \to limit select →from →where →group by →having →order by →limit
执行顺序:
f r o m → w h e r e → g r o u p b y → h a v i n g → s e l e c t → o r d e r b y → l i m i t from \ \to where\ \to group\ by\ \to having\ \to select\ \to order \ by\ \to limit from →where →group by →having →select →order by →limit
SELECT语句是SQL的查询。我们之前用的都是从单个数据库中检索单条语句。但如果涉及多个表格,查询多条语句,怎么办呢?SQL中使用子查询来解决这个问题。子查询是嵌套在其他查询中的查询。
如图,有三个表格,第一个表格是客户信息表 Customer,主键是 cust_id:顾客编号。第二个表格是订单表 Order,主要包括 order_num—订单号;order_date—订单日期;cust_id—顾客编号;第二个表格通过 cust_id—顾客ID与第一个表格关联。
第三个表格是订单物品存储信息 OrderItems,通过订单号 order—num与订单表 Order关联。
问题:我们想查询订购物品TNT2的所有顾客信息,怎么使用子查询?
&&如果用以前的单条查询,过程是这样的&&
第一步:
SELECT order_num
FROM orderitems
WHERE prod_id = 'TNT2';
SELECT cust_id
FROM orders
WHERE order_num IN (20005,20007);
SELECT cust_name,cust_contact
FROM customers
WHERE cust_id IN (10001,10004);
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 = 'TNT2'));
SELECT cust_name,cust_contact
FROM customers a,orders b,orderitems c
WHERE c.prod_id = 'TNT2' AND c.order_num = b.order_num AND b.cust_id = a.cust_id;
结果也是一样的。
使用子查询的另一种方法是创建计算字段。比如,要计算某个特定客户的订单数,我们可以这样查询:
SELECT COUNT(*) AS orders
FROM orders
WHERE cust_id = '10001';
SELECT c.cust_id,
cust_name,
(SELECT COUNT(*)
FROM orders o
WHERE o.cust_id = c.cust_id) AS orders
FROM customers c
ORDER BY orders desc;
select中的 orders 是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个客户执行一次。
结果:
创建子查询的技巧:
在语句的复杂性不断增加的情况下,子查询的创建很有技巧性。用子查询建立(和测试)查询的最可靠的方法是逐行进行,这与MySQL处理它们的方法非常相同。
问题描述:
本题中用到下面三个关系:
CARD 借书卡:CNO 卡号,NAME 姓名,CLASS 班级;
BOOKS 书籍:BNO 书号,BNAME 书名,AUTHOR 作者,PRICE 价格,QUANTITY数量;
BORROW 借书记录:CNO 借书卡号,BNO 书号,RDATE 还书日期;
备注:规定每人每种书只能借一本;库存册数随借书、还书而改变。
-- 创建借书卡表格
##############################################################
CREATE TABLE CARD(
CNO INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50),
CLASS VARCHAR(20)
)ENGINE = INNODB;
-- 创建图书表格
CREATE TABLE BOOKS(
BNO INT NOT NULL PRIMARY KEY,
BNAME VARCHAR(50),
AUTHOR VARCHAR(255),
PRICE DECIMAL(4,2),
QUANTITY INT
)ENGINE = INNODB;
-- 创建借书记录表格
CREATE TABLE BORROW(
CNO INT,
BNO INT,
RDATE DATETIME,
FOREIGN KEY (CNO) REFERENCES CARD(CNO),
FOREIGN KEY (BNO) REFERENCES BOOKS(BNO)
)ENGINE = INNODB;
#################################################################
-- 填充数据
############################################################################################################
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019001,'张长武','c01');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019002,'李云龙','c01');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019003,'特朗普','c03');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019004,'霍金','力01');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019005,'钱三强','力01');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019006,'杨振宁','力03');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019007,'华罗庚','m01');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019008,'陈景润','m02');
INSERT INTO CARD(CNO,NAME,CLASS) VALUES(2019009,'菲尔普斯','m03');
#############################################################################################################
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1001,'水浒','施耐庵',45.20,6);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1002,'计算机网络基础','张仲利',67.80,7);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1003,'网络安全概述','李丽',89.90,12);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1004,'计算方法','王毅',23.20,4);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1005,'计算方法习题册','张磊',21.00,8);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1006,'组合数学','Peter Harrington',69.00,3);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1007,'数据库技术技术及应用','鬼手',39.00,1);
INSERT INTO BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY) VALUES(1008,'统计学习方法','李航',30.00,3);
##############################################################################################################
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1005,'2019-06-20');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1006,'2019-06-29');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1003,'2019-02-01');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1002,'2019-07-02');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1007,'2019-08-02');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019005,1004,'2019-06-30');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019009,1004,'2019-06-26');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019009,1006,'2019-07-23');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019006,1004,'2019-03-12');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019001,1001,'2019-10-27');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019001,1004,'2019-09-10');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019002,1001,'2019-08-12');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019002,1006,'2019-05-20');
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019003,1001,'2019-06-20');
###############################################################################################################
SELECT * FROM CARD;
SELECT * FROM BOOKS;
SELECT * FROM BORROW;
###############################################################################################################
-- 一、找出借书超过5本的读者,输出借书卡号、借书人姓名以及所借图书册数
SELECT C.CNO,NAME,COUNT(BNO)
FROM BORROW B INNER JOIN CARD C
ON B.CNO = C.CNO
GROUP BY CNO
HAVING COUNT(BNO) > 5;
-- 二、查询借阅了“水浒”一书的读者,输出姓名及班级
SELECT NAME,CLASS
FROM CARD
WHERE CNO IN (SELECT CNO
FROM BORROW
WHERE BNO IN (SELECT BNO
FROM BOOKS
WHERE BNAME = '水浒'));
-- 三、查询过期未还的图书,输出借阅者(卡号)、书号及还书日期
-- current_date()返回当前的日期和时间
SELECT CNO,BNO,RDATE
FROM BORROW
WHERE RDATE < current_date();
-- 四、查询书名包括“网络”关键词的图书,输出书号、书名、作者
SELECT BNO,BNAME,AUTHOR
FROM BOOKS
WHERE BNAME LIKE '%网络%';
-- 五、查询现有图书中价格最高的图书、输出书名及作者
SELECT BNO,AUTHOR,PRICE
FROM BOOKS
ORDER BY PRICE DESC LIMIT 0,1;
-- 六、查询当前借了“计算方法”但没有借“计算方法习题集”的读者,输出其借书卡号,并按卡号降序排序输出
SELECT a.CNO
FROM BORROW a ,BOOKS b
WHERE b.BNAME = '计算方法' AND a.BNO = b.BNO
AND NOT EXISTS(
SELECT *
FROM BORROW a1 , BOOKS b1
WHERE a1.BNO = b1.BNO AND b1.BNAME = '计算方法习题集' AND a1.CNO = a.CNO);
-- 七、将“c01”班的同学所借图书的还期延长一周
SELECT b.CNO,BNO,DATE_ADD(RDATE,INTERVAL 7 DAY)
FROM BORROW b INNER JOIN CARD c
WHERE b.CNO = c.CNO AND c.CLASS = 'c01';
-- 八、从BOOKS表中删除当前无人借阅的图书记录
DELETE FROM BOOKS WHERE BNO = (SELECT c.BNO
FROM (SELECT BNO FROM BOOKS WHERE BNO NOT IN (SELECT BNO
FROM BORROW)) AS c);
SELECT * FROM BOOKS;
-- 九、如果经常按书名查阅图书信息,请建立合适的索引
####################报错###########################
CREATE CLUSTERED INDEX IDX_BOOKS_BNAME ON BOOKS(BNAME)
####################报错###########################
-- 十、在BORROW表上建立一个触发器,完成如下功能:如果读者借阅的书名是"数据库技术及应用",就将该读者的借阅记录保存在BORROW_SAVE表中(注ORROW_SAVE表结构同BORROW
####################-报错-###########################
CREATE TRIGGER TR_SAVE ON BORROW
FOR INSERT,UPDATE
AS
IF @ @ROWCOUNT > 0
INSERT BORROW_SAVE SELECT i.*
FROM INSERTED i,BOOKS b
WHERE i,BNO = b.BNO
AND b.BNAME = '数据库技术及应用';
####################-报错-###########################
-- 十一、建立一个视图,显示“力01”班学生的借书信息(只要求显示姓名和书名)
INSERT INTO BORROW(CNO,BNO,RDATE) VALUES(2019004,1001,'2019-07-20');
DELETE FROM BORROW WHERE CNO = 2019004;
#######################################################################
SELECT NAME,BNAME
FROM CARD c ,BORROW b,BOOKS o
WHERE c.CNO = b.CNO AND CLASS = '力01' AND b.BNO = o.BNO;
#######################################################################
-- 十二、查询当前同时借有“计算方法”和“组合数学”两本书的读者,输出其借书卡号,并按卡号升序排序输
SELECT a.CNO
FROM (SELECT CNO FROM BORROW WHERE BNO IN (SELECT BNO FROM BOOKS WHERE BNAME = '计算方法')) AS a,
(SELECT CNO FROM BORROW WHERE BNO IN (SELECT BNO FROM BOOKS WHERE BNAME = '组合数学'))AS b
WHERE a.CNO = b.CNO;
-- 十三、假定在建BOOKS表格时没有定义主码,写出为BOOKS表追加定义主码的语句
ALTER TABLE BOOKS ADD PRIMARY KEY(BNO);
-- 十四、将NAME最大列宽增加到60个字符(假定原为50个字符)
ALTER TABLE CARD MODIFY NAME VARCHAR(60);
-- 十五、为该表增加1列NAME(系名),可变长度,最大字符20个
ALTER TABLE CARD ADD 系名 VARCHAR(20);
SELECT * FROM CARD;
参考资料:
1.《MySQL必知必会》 Ben Forta;
2.sql面试题(学生表_课程表_成绩表_教师表):https://www.cnblogs.com/qixuejia/p/3637735.html
3.常用数学符号的 LaTeX 表示方法:http://mohu.org/info/symbols/symbols.htm