关于主键和外键:
简而言之,SQL的主键和外键就是起约束作用。
关系型数据库中一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性就可以成为一个主键。
成绩表中单一一个属性无法唯一标识一条记录,学号和课程编号的组合才可以唯一标识一条记录,所以学号和课程编号的属性组是一个主键。
成绩表中的学号不是成绩表中的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键;同理,成绩表中的课程号是课程表的外键。
定义:
1 | 主键:唯一标识一条记录,不能有重复,不允许为空。 |
作用:
1 | 主键:用来保证数据完整性 |
个数:
1 | 主键:主键只能有一个。 |
常用数据库查询操作
数据库查询
数据库操作的原理就是将需求翻译成数据库语言
简单的单表查询操作
查询“数据库原理”课程的学分;
1 | SELECT cname,credit FROM course WHERE cname = ‘数据库原理’ |
查询选修了课程编号为“C01”的学生的学号和成绩,并将成绩按降序输出;
1 | SELECT sno,grade FROM sc WHERE cno = ‘C01’ ORDER BY grade DESC; |
查询学号为“31401”的学生选修的课程编号和成绩;
1 | SELECT cno,grade FROM sc WHERE sno = ‘31401’ |
查询选修了课程编号为“C01”且成绩高于85分的学生的学号和成绩;
1 | SELECT sno, grade FROM sc WHERE cno = 'C01' AND grade > 85; |
简单的多表的连接查询
由于学号和成绩分别在student和sc表中,所以这是多表查询,多表查询注意隐含条件的表达
查询选修了课程编号为“C01”且成绩高于85分的学生的学号、姓名和成绩;
1 | SELECT sc.sno, sname, grade FROM student, sc WHERE student.sno = sc.sno AND cno = 'C01' AND grade >85; |
查询所有学生的学号、姓名、选修的课程名称和成绩;
1 | SELECT sc.sno, sname, cname, grade FROM student, sc, course WHERE sc.sno = student.sno AND sc.cno = course.cno; |
复杂的查询操作
GROUP BY用于合计函数,根据一个或多个列对结果集进行分组,比如这题想要得到的是 学生 的 课程总和 ,所以GROUP BY学生,合计课程号cno
查询至少选了三门课程的学生的学号和姓名;
1 | SELECT sc.sno, sname FROM sc, student |
GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组
查询所有学生的学号和他选修课程的最高成绩,要求他的选修课程中没有成绩为空的;
1 | SELECT sno, max(grade) FROM sc GROUP BY sno HAVING MIN(grade) > 0; |
带有IN、比较符的嵌套查询
查询修了数据库原理的学生的学号和姓名;
1 | SELECT sc.sno, sname FROM student, sc |
inner join表示输出前后两个表的共同部分on【共同的志向条件】
查询没有选修数据库原理的学生的学号和姓名;
1 | SELECT sno, sname FROM student |
查询至少选修了【学号为“31401”的学生所选修的所有课程】的学生的学号和姓名;
EXCEPT 仅返回那些不存在于第二个 SELECT 语句结果的记录(差集)
DISTINCT 关键字同 SELECT 语句一起使用,可以去除所有重复记录,只返回唯一项
EXISTS 和 NOT EXISTS 表示是否存在,只返回True/False
1 | select distinct sno, sname |
但是MYSQL没有except关键字,只能用建立视图解决问题:
视图的优点:简单、安全、数据独立
左连接where只影向右表,右连接where只影响左表
「select * from tbl1 Left Join tbl2 where tbl1.ID = tbl2.ID」
左连接后的检索结果是显示tbl1的所有数据和tbl2中满足where 条件的数据
1 | create view temp as select distinct cno from sc where sno=’31401’; |
由浅入深多表查询
多表连接分为三类
- 内连接:(joind,inner join)
- 外连接:(left join,left outer join,right join,right outer join,union)
- 交叉连接:(cross join)
一、内连接语法:
对内连接
1 | SELECT table1.column, table2.column |
例:查询每个员工的工号,姓名,工资,部门名和工作地点
1 | SELECT empno, ename, sal, dname, loc |
内连接的另一种写法:
1 | SELECT empno,ename,job,sal,dept.deptno,dname,loc |
二、外链接语法
对外连接
1 | SELECT table1.column, table2.column --右外连接 |
1 | SELECT table1.column, table2.column --左外连接 |
左外连接就是把左边的表内容全列出来,右边的只显示相同的部分,不存在的写NULL。
右外连接就是把右边表内容全列出来,左边只显示相同的部分,不存在的写NULL。
全连接就是完整返回左右表所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
例:查询每个员工的工号,姓名,工资,部门名和工作地点
1 | SELECT empno,ename,job,sal,dept.deptno,dname,loc --右外连接 |
另一种写法:
1 | SELECT empno,ename,job,sal,dept.deptno,dname,loc --右外连接 |
左外链接:
1 | SELECT empno,ename,job,sal,dept.deptno,dname,loc --左外连接 |
全连接:
1 | SELECT empno,ename,job,sal,d.deptno,dname,loc |
三、交叉连接(笛卡尔积)
实际应用中还有这样一种情形,想得到A,B记录的排列组合,即笛卡儿积,这个就不好用集合和元素来表示了。需要用到cross join
1 | SELECT empno,ename,dname |
数据库ODBC接口
利用C语言编程实现简单的数据库应用程序,掌握基于ODBC的数据库访问的基本原理和方法
1 |
|
数据库完整性与安全性
定义各基表的主键
1 | CREATE TABLE sc( |
删除和添加主键外键
1 | alter table course drop primary key; |
验证完整型约束:
分别向学生表、课程表插入具有相同学号和相同课程编号的学生数据和课程数据,验证其实体完整型约束。可以看出对student和course插入具有相同学号和课程编号的学生数据,都失败了,验证其实体完整型约束。
向学生课表插入一条课表编号课程表中没有的,验证参照完整型约束。插入失败。
定义存储过程
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
首先定义存储过程的结束符
1 | DELIMITER // |
声明存储过程
1 | create procedure demo_in_parameter(IN p_in int) |
存储过程开始和结束符号:
1 | BEGIN .... END |
变量赋值:
1 | SET @p_in=1 |
变量定义:
1 | DECLARE l_int int unsigned default 4000000; |
创建mysql存储过程、存储函数:
1 | create procedure 存储过程名(参数) |
存储过程体:
1 | create function 存储函数名(参数) |
示例:
创建数据库备份数据用于示例:
1 | create database db1; |
存储过程示例:「删除给定球员参加的所有比赛」
1 | delimiter // #将语句的结束符号从分毫;临时改为两个// |
调用存储过程:
1 | call sp_name[(传参)]; |
触发器
MySQL包含对触发器的支持。触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。
创建触发器语法:
1 | CREATE TRIGGER trigger_name #name |
示例:
假设系统中有两个表:
班级表 class(班级号 classID, 班内学生数 stuCount)
学生表 student(学号 stuID, 所属班级号 classID)
要创建触发器来使班级表中的班内学生数随着学生的添加自动更新,代码如下:
1 | DELIMITER $ |
数据库查询分析
深入理解分组查询
例题:把统计时间(statistic)
相同的,服务域(service_domain_moid)
a中的hd100
的数量和hd100s
的数量的和计算出来:
首先,我们把service_domain_moid
为a
的筛选出来,如下:
1 | SELECT hd100, hd100s FROM statistic |
其次: 我们把统计时间(statistic_time)
相同的分组,求和
1 | SELECT sum(hd100),sum(hd100s) |
最后,统计结果按降序排序:
1 | SELECT sum(hd100),sum(hd100s) |
GROUP BY
关键字可以将查询的结果按某个字段或多个字段进行分组,字段中值相等的为一组。其语法规则如下:
1 | GROUP BY 属性名 [HAVING 条件表达式] [WITH ROLLUP] |
ORDER BY
关键字可以对记录进行排序,其语法规则如下:
1 | ORDER BY 属性名 [ASC|DESC] |
数据库练习题:
01 按部门名称查询出员工的人数大于等于100的语句.(员工:emp_employee, 部门名称: department,员工姓名chnalias.)
1 | SELECT department FROM emp_employee |
02 把员工的姓名、性别和年龄显示出来。(员工表emp_employee,员工姓名:chnalias,年龄age,性别:gender。性别表:pub_gendar_info,性别名称:smpalias,与员工表的性别无关)
1 | SELECT e.chnalias, e.smpalias, e.age |
04 删除员工表中的姓名重复的数据,只保留重复数据中的一条数据。(员工表emp_employee,员工姓名:chnalias)
1 | DELETE FROM emp_employee |
AS
关键字为查询的字段起一个别名,例如上面的语句里,给MAX(id)
起个别名id
【AS关键字可省略】
05 查询出差表中的每个员工最近的出差时间和员工姓名(出差表emp_travel,员工姓名chnalias,出差时间godate)
1 | SELECT chnalias, godate FROM emp_travel ORDER BY godate DESC |
数据库查询练习
01 没有选修课程编号为C1
的学生姓名【多表查询】
首先找出C1
对应的chosen_class
表中的c_id
1 | SELECT chose_class.c_id FROM class, chosen_class |
然后找出没有选修C1的学生
1 | SELECT s_name FROM student, chose_class |
02 列出每门课程名称和平均成绩,并按照程序排序
先分组查找计算每门课对应的平均成绩:
1 | SELECT c_id, avg(grade) avggrade FROM chosen_class |
然后多表查询(名称+成绩)并按照程序排序:
1 | SELECT c.c_name, avggrade FROM( |
03 选了2门课以上的学生姓名
统计学生id对应的选课数
1 | SELECT s_id, count(*) countclass FROM chosen_class |
1 | SELECT s.s_name, b.countclass FROM( |