17 KiB
实验一
如何建表 create Table
CREATE TABLE hhh(
id int auto_increment primary key,
name varchar(30) not null,
age int ,
birthday date,
is_actiove boolean default true
);
create table 表名(
字段名 字段类型 约束条件,
字段名 字段类型 约束条件,
字段名 字段类型 约束条件
);
如何新增字段 Add column
Alter table role add age int not null
ALTER TABLE <表名> ADD <新字段名><数据类型>[约束条件];
如何增加数据
INSERT INTO products (maker, model, `type`) VALUES('A', '1001', 'pc'),
INSERT INTO 表名 (字段,字段2,.....)values (`数值1`,`数值2`,`数值3`),
DDL DML
DDL(Data Definition Language)语句: 数据定义语言,主要是进行定义/改变表的结构、数据类型、表之间的链接等操作。常用的语句关键字有 CREATE、DROP、ALTER 等。
CREATE TABLE 表名(
列名1 数据类型,
列名2 数据类型,
列名3 数据类型,
...
)
ALTER TABLE 表名;
eg:ALTER TABLE 表名 ADD 列名 数据类型;(添加一个列)
ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;(修改列名)
ALTER TABLE 表名 DROP 列名;
DROP TABLE 表名;
DROP DATABASE 数据库名;
DML(Data Manipulation Language)语句: 数据操纵语言,主要是对数据进行增加、删除、修改操作。常用的语句关键字有 INSERT、UPDATE、DELETE 等。
INSERT INTO 表名 (字段1,字段2,...) values (某值,某值,...),(某值,某值,...);
UPDATE 表名 SET 列名=新值 WHERE 限定条件;
DELETE FROM 表名 WHERE 限定条件;
Like
SELECT *
FROM customers
WHERE firstname LIKE '%a%';
%:匹配任意字符(包括零个字符)。_:匹配单个字符。
实验二
四种连接 等值连接 自然连接 相关子查询 非相关子查询
join(equal join: comma where, join on) 等值连接
SELECT 列1, 列2, ...
FROM 表1
[INNER] JOIN 表2 -- INNER可省略,默认就是内连接
ON 表1.关联列 = 表2.关联列 -- 明确分离连接条件
WHERE 表1.过滤条件 = xxx -- 仅存放过滤条件,可读性更强
[AND 表2.过滤条件 = xxx];
select name ,age
from role join user
on role.id=user.id
where role.name="郑笃实" and ....
-- 例子
-- 1. 老式逗号+WHERE写法
SELECT s.student_name, sc.subject, sc.score
FROM student s, score sc -- 逗号分隔两张表,给表起别名简化书写
WHERE s.student_id = sc.student_id -- 等值连接条件
AND sc.score >= 60; -- 过滤及格成绩
-- 2. 标准JOIN ON写法(推荐)
SELECT s.student_name, sc.subject, sc.score
FROM student s
JOIN score sc
ON s.student_id = sc.student_id -- 明确连接条件
WHERE sc.score >= 60; -- 明确过滤条件
natural join(natural join or join using()) 自然连接
-- 2. 标准JOIN ON写法(推荐)(必须两张表中均存在该列,且列名完全一致)(必须两张表中均存在该列,且列名完全一致)
SELECT s.student_name, sc.subject, sc.score
FROM student s
JOIN score sc
USING (sc.student_id) -- 明确连接条件 -- ON s.student_id = sc.student_id
WHERE sc.score >= 60; -- 明确过滤条件
-- 其实就跟join on一样而已 只不过是 孔乙己的茴的四种写法罢了......
corelated subquery 相关子查询
相关子查询(又称关联子查询)是依赖外部查询的列值才能执行的子查询,无法独立运行(子查询中会引用外部查询的表列)。
-- 需求:查询「个人成绩大于其所在班级平均成绩」的学生姓名和对应科目、成绩
SELECT s.student_name, sc.subject, sc.score
FROM student s
JOIN score sc ON s.student_id = sc.student_id
-- 外部查询的s.class_id传入子查询,作为平均成绩的计算条件
WHERE sc.score > (
SELECT AVG(sc2.score) -- 子查询:计算当前学生所在班级的平均成绩
FROM score sc2
JOIN student s2 ON sc2.student_id = s2.student_id
WHERE s2.class_id = s.class_id -- 引用外部查询的s.class_id,依赖外部查询
);
子查询 就是一个sql 嵌套在另一个sql里面
noncorelated subquery 非相关子查询
是不依赖外部查询的子查询,可以独立执行并返回一个结果集,外部查询直接使用该结果集进行后续计算,是最常用的子查询类型。
-- 需求:查询“数学科目满分(100分)”的学生姓名
SELECT student_name
FROM student
WHERE student_id = ( -- 子查询返回单行结果时,用=匹配
SELECT student_id
FROM score
WHERE subject = '数学' AND score = 100
LIMIT 1 -- 确保子查询返回单行
);
-- 这个其实就是大多数时候 写的sql语句 (虽然工作中 一般都不这么手写原生的sql语句hhhh)
UNION 大法
有思路但是不知道这么写?
有多张表但是不知道这么写在一个sql里面??
为什么要写在一个sql里面?? UNION大法帮助你
SELECT p.model, pc.price
FROM products p
JOIN pcs pc ON pc.model = p.model
WHERE p.maker = 'B'
UNION
SELECT p.model, l.price
FROM products p
JOIN laptops l ON l.model = p.model
WHERE p.maker = 'B'
UNION
SELECT p.model, pr.price
FROM products p
JOIN printers pr ON pr.model = p.model
WHERE p.maker = 'B';
-- left join
SELECT p.model, COALESCE(pc.price, l.price, pr.price) AS price
FROM products p
LEFT JOIN pcs pc ON pc.model = p.model
LEFT JOIN laptops l ON l.model = p.model
LEFT JOIN printers pr ON pr.model = p.model
WHERE p.maker = 'B';
-- 谁最清晰一目了然
小tip
SELECT 分组列, 聚合函数(...) FROM 表 WHERE 行条件 GROUP BY 分组列 HAVING 组条件(通常含聚合函数) ORDER BY ... 排序顺序
- GROUP BY 不一定要配合having
- GROUP BY 是按照....分组 就是 类似去重了
- HAVING 就是对于这个分组的限制 一般都是
Find those makers of at least two different computers (PCs or laptops) with speeds of at least 2.80.
SELECT maker
FROM (
SELECT p.maker, p.model
FROM products p
JOIN pcs pc ON pc.model = p.model
WHERE pc.speed >= 2.80
UNION
SELECT p.maker, p.model
FROM products p
JOIN laptops l ON l.model = p.model
WHERE l.speed >= 2.80
) t
GROUP BY maker
HAVING COUNT(DISTINCT model) >= 2;
8) Find all customer's information purchased model 1001 and 1002.
(找出同时买过 1001 和 1002 的客户信息)
SELECT c.*
FROM customers c
JOIN sales s ON s.customer_id = c.customer_id
WHERE s.model IN ('1001', '1002')
GROUP BY c.customer_id, c.firstname, c.lastname, c.city, c.address, c.email
HAVING COUNT(DISTINCT s.model) = 2;
记忆点:
WHERE model IN (...)先筛两种型号,再用HAVING COUNT(DISTINCT model)=2保证“两种都买过”。
9) Find all laptops whose hard disk is greater than all PCs' hard disk.
(找出所有 laptop,使其 hd 大于所有 PC 的 hd)
最稳写法(用 MAX):
SELECT *
FROM laptops
WHERE hd > (SELECT MAX(hd) FROM pcs);
(也可以写 ALL:)
SELECT *
FROM laptops
WHERE hd > ALL (SELECT hd FROM pcs);
10) Find all customer that not purchase any products.
(找出从未购买过任何产品的客户)
方法1:LEFT JOIN
SELECT c.*
FROM customers c
LEFT JOIN sales s ON s.customer_id = c.customer_id
WHERE s.customer_id IS NULL;
方法2:NOT EXISTS
SELECT c.*
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM sales s
WHERE s.customer_id = c.customer_id
);
实验三
触发器 Trigger
//创建触发器
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON table_name FOR EACH ROW
BEGIN
-- 觸發器執行的SQL語句
statement1;
statement2;
statement3;
END;
// 查看触发器
show trigger
//删除触发器
drop trigger schemaName.triggerName
(1)定义AFTER行级触发器,当电脑表PCS的价格发生变化后就自动在价格变化表pcprice_log中增加一条相应记录
create trigger abbc
after update on 电脑表 for each row
begin
insert into log values(old.values,new.values,,,)
end
(2)定义AFTER行级触发器,当删除顾客表Customer中的一个顾客信息后,同时删除购买信息表Sales中该顾客的所有购买记录
create trigger abbb
begin DELETE on customer for each row
begin
delete from sales where contomerid=old.contomerid
end
(3)需要对在表上进行DML操作的用户进行安全检查,看是否具有合适的特权
(5)如果一个特定客户支付的总金额高于10000,该客户的折扣小于10%然后将该客户的折扣设置为10%。在每一个插入到销售表之后,该触发器应该被激活。
create trigger asdgfsd
after insert on 表 for each row
begin
DECLARE total_paid DECIMAL(12,2);
SELECT SUM(paid)
INTO total_paid
FROM Sales
WHERE customer_id = NEW.customer_id;
IF total_paid > 10000 THEN
UPDATE Customer
SET discount = 0.1000
WHERE customer_id = NEW.customer_id
AND discount < 0.1000;
END IF;
end
(6)在客户中添加一个新的"AllPaid"栏,如果插入或更新或删除一个销售元组,那么修改该客户的"AllPaid"的值
CREATE TRIGGER trg_sales_allpaid_ins
AFTER INSERT ON Sales
FOR EACH ROW
BEGIN
UPDATE Customer
SET AllPaid = AllPaid + NEW.paid
WHERE customer_id = NEW.customer_id;
END
CREATE TRIGGER trg_sales_allpaid_ins
AFTER update ON Sales
FOR EACH ROW
BEGIN
UPDATE Customer
SET AllPaid = AllPaid + NEW.paid
WHERE customer_id = NEW.customer_id;
END$$
CREATE TRIGGER trg_sales_allpaid_ins
AFTER delete ON Sales
FOR EACH ROW
BEGIN
UPDATE Customer
SET AllPaid = AllPaid + NEW.paid
WHERE customer_id = NEW.customer_id;
END$$
关于触发器的new虚拟表格和old虚拟表格
当进行insert的时候,new表格可以获取插入的数据。
当进行delete的时候,old表格可以获取被删除行的数据。
当进行update的时候,new表格中是修改后的数据,old表格中是被修改行的数据。
实验四
存储过程
我们之前所学习的 MySQL 语句都是针对一个表或几个表的单条 SQL 语句,但是在数据库的实际操作中,经常会有需要多条 SQL 语句处理多个表才能完成的操作。
例如: 张三工资是否大于 所属部门的平均工资 先知道张三部门,计算平均工资,是否大于
存储过程是一组为了完成特定功能的 SQL 语句集合
- 减少网络传输量(客户端不需要把所有的 SQL 语句通过网络发给服务器)
- 减少了 SQL 语句暴露在 网上的风险,也提高了数据查询的安全性
- 简化操作,提高了sql语句的重用性,减少了开发程序员的压力
- 减少操作过程中的失误,提高效率
# 因为存储过程中,为了区分多条SQL每个SQL需要使用 ;分号作为结束符号
# 而 Mysql ;分号是几乎所有sql语言的结束语 BEGIN --- END 中的分号会导致声明存储过程的语法结束,报错;
# 所以:需要使用 DELTMITER 来改变MYSQL的结束符号 (这里的// 并不是固定,而是一个不会造成其它影响的一个特殊符号 可以随意更改Mysql的结束符号
DELIMITER //
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
DECLARE
#存储过程体
END //
# 因为 DELIMITER 更改了Mysql的结束符所以执行: END // 结束存储过程声明
DELIMITER ;
# 为了存储过程中的多SQL ; 分号结束符不会导致存储过程声明的中断.
# DELTMITER 改变了Mysql的结束符, 当存储过程声明结束,为了不影响正常使用,建议将Mysql默认结束符 ; 更改回去,避免造成其它影响
# 类似于Java 的函数定义语法: 修饰符 返回类型 方法名(参数类型 参数名,....){ 方法体; } 学过编程语言的这里应该都不是难事.
| 参数类型 | 作用 | 传递方向 | 特点说明 |
|---|---|---|---|
| IN | 输入参数,给存储过程传入值 | 调用时传入,过程内只读 | 过程内不能修改传入值(改变不影响调用者) |
| OUT | 输出参数,存储过程把值返回给调用者 | 过程内写,调用后拿结果 | 调用时不传值,过程内部给它赋值,调用后取出值 |
| INOUT | 输入输出参数,既传入又返回结果 | 调用时传入,过程内读写 | 过程内可以读取初始值并修改,调用结束后返回新值 |
例子
CREATE PROCEDURE selId(IN ID INt, NNAME DOUBLE) # 如果只有一个输入参数 关键字in 可以省略
BEGIN
-- SELECT * FROM employees WHERE employee_id = 100;
SELECT * FROM employees WHERE employee_id = ID;
END
#调用: 方式一
CALL selId(100);
#调用: 方式二
CALL selId('100');
#调用: 方式三,通过创建变量方式进行入参赋值...
SET @eid = 100
CALL selId(@eid);
存储函数
-- 存储函数 和 存储过程声明语法大致也相同,
CREATE FUNCTION 函数名([IN] 参数名 参数类型)
RETURNS 返回值类型
[characteristics...]
BEGIN
-- 函数体
-- 函数体中存在 RETURN 语句
END
- 参数列表: 存储函数 声明比较严格,参数只能是
IN入参默认也是IN 所以可以不用声明参数 IN 更加符合编程语言的规范. - RETURNS 表示存储函数的 返回类型, 存储函数只能有一个返回值. 且
必须有一个返回值. - [characteristics…] 和存储过程一样,声明对函数内部的声明约束.
- BEGIN…END 和存储过程一样包含方法体,如果只有一条语句,也可以省略.
# 根据员工的id 查询员工的姓名
DELIMITER $$
CREATE FUNCTION find(empid INT)
RETURNS VARCHAR(60)
BEGIN
DECLARE name VARCHAR(50);
SELECT CONCAT(firstname, lastname) INTO name
FROM employees
WHERE id = empid;
RETURN name;
END$$
DELIMITER ;
# 调用函数
select find(123);
select find(3123);
存储函数(FUNCTION)不支持
OUT / INOUT参数,只能有IN参数(输入参数)。
#编写一个存储过程 RateModel(cid, m, r):
#将客户 cid 对型号 m 的新评分 r 插入到表 Ratings 中;
#字段 rating_time 应设置为当前日期与时间;
#该过程还需要更新表 Products 中型号 m 的:
#number_of_ratings(评分次数)
#total_rating(评分总和)
CREATE PROCEDURE RateModel(IN cid VARCHAR(20), IN m VARCHAR(20), IN r INT)
BEGIN
DECLARE v_cnt INT;
DECLARE v_sum INT;
-- 1) 插入一条新评分(历史保留)
INSERT INTO Ratings(customer_id, model, rating_time, rating)
VALUES (cid, m, NOW(), r);
-- 2) 重新统计该型号 m 的评分次数和总分(只统计非 NULL)
SELECT COUNT(rating),SUM(rating)
INTO v_cnt, v_sum
FROM Ratings
WHERE model = m AND rating IS NOT NULL;
-- 3) 更新 Products
UPDATE Products
SET number_of_ratings = v_cnt,
total_rating = IF(v_cnt = 0, NULL, v_sum)
WHERE model = m;
END
#编写一个存储过程 DeleteRating(cid, m):
#通过插入一条新记录的方式来“删除/取消”客户 cid 对型号 m 的评分;
#实现方式:在 Ratings 表中插入一条记录,其中:
#customer_id = cid
#model = m
#rating = NULL
#字段 rating_time 仍应设置为当前日期与时间。
create procedure DeleteRating(in cid int, in m varchar(50))
begin
Functional Dependency and Normal Form
请给出 函数依赖 (Functional Dependency) 的定义,并解释 平凡函数依赖 (Trivial FD) 和 非平凡函数依赖 (Non-trivial FD)。
求候选码
Let relation (i.e. table) R(ABCDEFGH) have the following functional dependencies:
A → B
CH → A
B → E
BD → C
EG → H
DE → F
Find all keys of relation R(ABCDEFG) with functional dependencies.
#这个FD的所有的候选码
-
先算出所有的RHS
- RHS就是在等号右边的 RHS={B A E C H F }
-
所以我们要以不在RHS的字母开始 也就是C D
-
算{C D}+ 就是看 C D 能推出来什么 看起来什么都不能推出来 只有自己
-
那就算三个的 {ACD} 能推出全集 那就是 acd是一个key
- 然后发现BCD也是
-
发现剩下的三位的不够了
-
看四位的CDGH CDEG 是
-
最终总结u所有的candidate key 是 {ADG} {BDG} {CDGH} {CDEG}
-
判断下面是否有冗余的
-
邪修法门
- L 只在左边出现过的 (包的)
- R 只在右边出现过的 (不可能)
- LR 两边都出现过的 (有可能成为候选码)
判断是否符合BCNF
The relation R has the attributes R(ABCD) and the functional dependencies F={AB→C, C→D, D→A}, Is the relation R in BCNF? Briefly explain your answer.
求最小依赖集