Files
StudyNote/山东理工大学/数据库/作业.md
2026-02-13 23:38:38 +08:00

17 KiB
Raw Blame History

实验一

如何建表 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

DDLData Definition Language语句 数据定义语言,主要是进行定义/改变表的结构、数据类型、表之间的链接等操作。常用的语句关键字有 CREATE、DROP、ALTER 等。

CREATE TABLE 表名(
列名1 数据类型,
列名2 数据类型,
列名3 数据类型,
...
)

ALTER TABLE 表名;
egALTER TABLE 表名 ADD 列名 数据类型;(添加一个列)
    ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;(修改列名)
    ALTER TABLE 表名 DROP 列名;

DROP TABLE 表名;
DROP DATABASE 数据库名;

DMLData 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.

(找出从未购买过任何产品的客户)

方法1LEFT JOIN

SELECT c.*
FROM customers c
LEFT JOIN sales s ON s.customer_id = c.customer_id
WHERE s.customer_id IS NULL;

方法2NOT 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的所有的候选码
  1. 先算出所有的RHS

    1. RHS就是在等号右边的 RHS={B A E C H F }
  2. 所以我们要以不在RHS的字母开始 也就是C D

  3. 算{C D}+ 就是看 C D 能推出来什么 看起来什么都不能推出来 只有自己

  4. 那就算三个的 {ACD} 能推出全集 那就是 acd是一个key

    1. 然后发现BCD也是
  5. 发现剩下的三位的不够了

  6. 看四位的CDGH CDEG 是

  7. 最终总结u所有的candidate key 是 {ADG} {BDG} {CDGH} {CDEG}

  8. 判断下面是否有冗余的

  9. 邪修法门

    1. L 只在左边出现过的 (包的)
    2. R 只在右边出现过的 (不可能)
    3. 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.

求最小依赖集