考试大纲
- 第一章 5-10分 10分
- 第二章 5-10分 10分
- 1、计算 除/象集
- 2、连接
- 3、选择
- 4、投影
- 5、代数表达式综合
- 第三章 20分 20分
- 1、修改表的信息用UPDATE
- 2、创建索引
- 3、有效性检查CHECK 和 缺省值
- 4、数据库表修改SQL语句
- 5、挑选表中的数据赋给中间变量(一般用于存储过程)
- 6、分组之后求每一组的数量----就直接COUNT(*) 数行就用这个
- 7、数据删除(删除一行)
- 8、创建触发器
- 9、权限
- 10、备份
- 11、通配符
- 12、视图
- 综合编程题
- 第四章 20-30 20分
- 1、函数的依赖关系
- 2、属于第几范式
- 3、对关系进行规范化处理,使其满足第...范式
- 4、对表格进行数据操作 是否会有数据异常?说明会有什么异常?
- 第五章 10-20 10分
- 1、数据库系统安全模型
- 2、 数据库存储结构
- 3、 索引
- 4、事务:求出执行结果,并说明原因
- 第六章 20-30 30分
- 读JDBC代码
- ODBC?
- 存储过程(一个存储过程,类似于函数,无返回类型,有中间变量要declare)
- 触发器---两个表,一个函数(返回值为触发器类型),一个触发器
- 游标(一个函数,里面使用了游标的语法,返回类型boolean/viod,有中间变量要declare)
题型:
分析题、应用题、编程题、设计题
设计题两道 每题十分,其他五分一提
分数分布:
第一章 5-10分 10分
1.5 我课堂没有讲,也属于考核范围,请使用教材复习
概念
第二章 5-10分 10分
关系模型 关系运算是重点,概念和运算
- 关系代数表达式:
先看要求哪些列,而这些列是在哪个表中的
有”至少“ ”全部“要用到÷
没有挑选的话直接连接关系,不用 π - 关系模型组成:数据结构,数据操作,数据关系约束
- 关系的特征:
- 表中每行存储实体的一个实例数据
- 表中每列包含实体的一项属性数据
- 表中单元格只能存储单个值
- 不允许有重复的行、列
- 列、行顺序可任意
1、计算 除/象集
先看重合的列,R中A的取值对应的BC的对,哪个全部涵盖了S,最后答案就是哪个A
2、连接
只有一个蝴蝶结是自然连接
自然连接和等值连接类似,自然连接是去掉了相等的行
3、选择
右下角是选择条件,右边的括号里是整体的范围,一般是表格
4、投影
5、代数表达式综合
第三问:
查找过程:要查学生姓名,先写个 兀NAME。首先在C表中找程军对应的所有课程号(得到一个符合条件的只有C#列的表),然后看SC表课程号对应的学号(选择出SC表中的S#,C#,让这两列的表÷C表中只有符合条件的C#的列的表,得出的就是只有S#这一列的表),所以学号找到了,就去S表中(连接运算)找学号对应的姓名。
第5问:
至少修2门课的学生号,没有说是哪两门课---->用笛卡尔积
SC×SC,就有六列,选择条件----S#=S# C≠C 然后选择S就好了
第6问:
全部学生选修了的课程号:就让SC前两列表÷S的学号表,就得全部学生都选了的课程号表
第三章 20分 20分
SQL语句 都用单引号
- 两个主键可以写在一起,CONSTRANIT 表名_PK PRIMARY KEY(列,列);
两个外键要分开写:CONSTRAINT 列名_FK FOREIGN KEY(列) REFERENCES 表(列); - 预算在两万—五万之间:BETWEEN …AND…
- Insert into 后面如果已经有了中间变量的值,就直接values(放中间变量)
没有就要 后面加 SELECE - 触发器要有返回值NEW 或OLD
1、修改表的信息用UPDATE
数据更新中要有WHERE条件,否则会更新所有行中该列的值
在学生信息表中Student,“赵东”原有的Email数据为空,现在需要更新为“zhaodong@163.com”,出生日期更新为‘1999-11-15’
UPDATE Student
SET Email='zhaodong@163.com',Birthday='1999-11-15'
WHERE StudentName='赵东';
2、创建索引
CREATE INDEX 索引名 ON 表(列)
CREATE INDEX Birthday_Idx ON Student (Birthday);
3、有效性检查CHECK 和 缺省值
CourseType varchar(10) NULL CHECK(CourseType IN('基础课','专业课','选修课')
TestMethod varchar(10) NULL DEFAULE '闭卷考试'
4、数据库表修改SQL语句
1、语句类型
- 增加新列或列完整性约束:ADD
ALTER TABLE <表名> ADD <新列名称> <数据类型> 或[完整性约束];
- 删除:DROP (列或完整性约束)
ALTER TABLE <表名> DROP COLUMN<列名>;
ALTER TABLE <表名> DROP CONSTRANIT<完整性约束名>;
- 修改表名称,列名称:RENAME TO
ALTER TABLE <表名> RENAME TO <新表名>;
ALTER TABLE <表名> RENAME <原列名> TO <新列名>;
- 修改列的数据类型:ALTER
ALTER TABLE <表名> ALTER COLUMN<列名> TYPE<新的数据类型>;
5、挑选表中的数据赋给中间变量(一般用于存储过程)
SELECT AVG(age) into avg_age from student where class=class_name;
6、分组之后求每一组的数量----就直接COUNT(*) 数行就用这个
SELECT 供应商代码
FROM 供应零件
GROUP BY 供应商代码
HAVING COUNT(*)>5;
7、数据删除(删除一行)
WHERE条件
删除姓名为张亮的学生数据
DELETE
FROM Student
WHERE StudentName='张亮';
8、创建触发器
CREATE TRIGGER score_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON student_score
FOR EACH ROW EXECUTE PROCEDURE scour_audit();
9、权限
grant <权限名> on <对象名> to {数据库用户名|用户角色名};
revoke <权限名> on <对象名> from {数据库用户名|用户角色名};
deny <权限名> on <对象名> to {数据库用户名|用户角色名};
10、备份
备份SAMPLE数据库到一个G磁盘的根目录文件Sample.bak中。
BACKUP DATABASE SAMPLE TO DISK = ‘G:\Sample.bak';
从存储备份文件中恢复SAMPLE数据库。
RESTORE DATABASE SAMPLE FROM DISK = ‘G:\Sample.bak';
11、通配符
WHERE Email LIKE ’%@163.com’;
12、视图
create view <> as + 查询语句
综合编程题
- “打⻋软件管理系统”有如下的表⸺
◦ Customer(cid,cname,cphone)
◦ Driver(did,dname,dlevel)
◦ Assign(aid,cid,did,estcost,actualcost,status)//派单表,派单编号、客⼾id,司机id,预估⾦
额,实际⾦额,状态(状态为“司机已接单”,“司机正在赶来”,“已完成”)
a. 给出assign表的创建表语句以及参照完整性语句,其中status默认为“司机已接单”
注意:id号都为int,虽然只写了一个表,但是也要注意和其他表的关系,别忘表中的外键
CREATE TABLE Assign (
aid INT PRIMARY KEY,
cid INT,
did INT,
estcost DECIMAL(10,2),
actualcost DECIMAL(10,2),
status VARCHAR(20) DEFAULT '司机已接单' CHECK(status IN '司机已结单','司机正在赶来','已完成')
constraint cid_fk FOREIGN KEY (cid) REFERENCES Customer(cid) ON DELETE CASC
constraint did_fk FOREIGN KEY (did) REFERENCES Driver(did) ON DELETE CASCAD
);
查询未完成的派单的司机的姓名、编号
注意:原来还能写: !=
SELECT Driver.dname, Driver.did FROM Driver
INNER JOIN Assign ON Driver.did = Assign.did
WHERE Assign.status != '已完成';
查询司机已经完成的订单的实际⾦额的总数,查询结果为司机姓名、司机编号、总⾦额
注意:括号里也要写点
SELECT Driver.dname, Driver.did, SUM(Assign.actualcost)
FROM Driver
INNER JOIN Assign ON Driver.did = Assign.did
WHERE Assign.status = '已完成'
GROUP BY Driver.did, Driver.dname;
创建视图,供市场调研⼈员查看所有乘客的消费排序视图,给出顾客姓名、顾客电话、订单总数、订单总⾦额
注意:COUNT(*)就是数所有符合要求的行
CREATE VIEW CustomerPaymentSummary AS
SELECT Customer.cname, Customer.cphone, COUNT(*) AS order_count, SUM(Assign.a
FROM Customer
INNER JOIN Assign ON Customer.cid = Assign.cid
GROUP BY Customer.cid, Customer.cname, Customer.cphone
ORDER BY total_payment ;
第四章 20-30 20分
设计题出在本章 概念模型、逻辑模型和物理模型 数据库规范化设计 使用鸟足模型画图(和教材一致)、
1、函数的依赖关系
2、属于第几范式
- 第一范式(1NF):属性不可再分
- 第二范式(2NF):1NF的基础上,消除了属性部分依赖。(只有一个主键的话,都满足第二范式)
- 第三范式(3NF):第二范式的基础上,切断了属性传递函数依赖
- BCNF(巴斯-科德范式):所有函数依赖的决定因子都是候选键
- 第四范式(4NF):满足BCNF,并且消除了多值依赖
3、对关系进行规范化处理,使其满足第…范式
4、对表格进行数据操作 是否会有数据异常?说明会有什么异常?
第五章 10-20 10分
概念
数据库存储结构 索引结构 事务 并发 安全
1、数据库系统安全模型
- 身份验证:从应用系统层面确认登录用户是否是合法使用者
- 权限控制:从DBMS系统层面通过存取权限机制控制用户对数据的访问
- 系统防护:从操作系统层面提供的安全机制防范非法系统访问
- 加密存储:从数据存储层面通过加密算法对数据库中数据进行加密存储
2、 数据库存储结构
- 逻辑存储结构:面向数据库编程用户 基本单位 内存页
- 物理存储结构:面向DBA用户 基本单位 操作系统的文件块(OS块)
一个数据库物理上是将数据存储在若干数据文件中的,这些数据文件在操作系统的文件目录中组织与存储数据。
3、 索引
索引:一种数据记录定位结构。它将查询条件键值作为输入,能快速从索引结构(如索引表)获取该条件键值对应数据记录的位置指针,通过位置指针从关系表中找出结果集记录。
4、事务:求出执行结果,并说明原因
丢失更新–脏读—不可重复读取
第六章 20-30 30分
编写存储过程和触发器 应用程序编程访问数据库的相关技术(ODBC,JDBC),编写类似于 例6-20的程序(游标) JDBC
要看懂这个代码?
读JDBC代码
Class.forName(‘‘org.postgresql.Driver’’);PostgreSQL数据库驱动程序加载语句
Connection conn=DriverManager.getConnerction(URL,userName,passWord);创建connection对象,与数据库建立连接
Statement stmt=conn.createStatement();创建Statement对象
String sql=‘‘INSERT INTO…’’;
stmt.executeUpdate(sql);执行SQL语句
ResultSet rs=stmt.executeQuery(sql); 保存结果集
rs.close();关闭结果集对象
stmt.close();关闭执行对象
conn.close();关闭连接对象
Catch (SQLException e); 如果连接失败抛出错误
ODBC?
如图为ODBC的结构图,说明哪些是操作系统提供的,哪些是数据库⼚商提供的,数据源包含哪些信息?
- 操作系统:ODBC应⽤程序接⼝、ODBC驱动程序管理器
- 数据库厂商:驱动程序、数据源、DBMS
- 数据源包含:数据库位置、数据库名称及驱动程序等信息
存储过程(一个存储过程,类似于函数,无返回类型,有中间变量要declare)
封装一个过程,类似于定义一个函数,用的时候直接调用
有表Student(id,name,age,gender,class),编写存储过程get_class_info(class_name),统计班级信
息,在控制台输出查询的班级的平均年龄、男⽣⽐例、⼥⽣⽐例。
注意:有参数要写—IN 参数名 数据类型;别忘最后WHERE那里要有使用参数的条件
变量声明整形的一开始要赋初始值为0,是写在最后,也就是数据类型的后面
要哪个中间变量,一个一个选,记得要有INTO
无论统计男生还是女生,直接数所有行就可以。所以只要数行,就直接COUNT(*),因为数那几行后面有条件
把这个过程就看作是一个要完成的功能的c代码,想要什么就做什么。这道题是输出信息,所以设几个中间变量,表格就是工具,把从表中挑出来的值赋给中间变量
CREATE OR REPLACE PROCEDURE get_class_info(IN class_name VARCHAR) AS $$
DECLARE
avg_age INTEGER := 0;
male_count INTEGER := 0;
female_count INTEGER := 0;
student_count INTEGER := 0;
BEGIN
SELECT AVG(age) into avg_age from student where class=class_name;
SELECT Count(*) into male_count from student where class=class_name AND gender='男';
SELECT Count(*) into female_count from student where class=class_name AND gender='⼥';
student_count=male_count+female_count;
raise notice '平均年龄: %,男⽣⽐例:%,⼥⽣⽐例',avg_age, male_count/student_count,female_count/student_count
END;
$$ LANGUAGE plpgsql;
(1)创建客户余额信息表accounts
drop table if exists accounts;
create table accounts (
id SERIAL, primary key --客户ID号
username varchar(100) not null, --客户姓名
balance dec(15,2) not null, --账户余额
);
(2)创建客户转账信息表translists
drop table if exists translists;
create table translists (
trans_id SERIAL primary key, --转账记录ID
sendid int, --转出客户的ID
sendname varchar(100) not null, --转出客户的姓名
recid int, --转入客户的ID
recname varchar(100) not null, --转入客户的姓名
transdate timestamp(0), --转账时间
transamount dec(15,2) not null --转账金额
);
(3)为了测试程序,预先在客户余额信息表accounts中插入两条客户余额信息;
insert into accounts(username,balance) values('Bob',10000);
insert into accounts(username,balance) values('Alice',10000);
(4)创建存储过程
GET DIAGNOSTICS row_num:=ROW_COUNT;
注意INSERT INTO 表名(列名) 别忘了可以指定列来插入
//定义一个存储过程,类似于函数声明
CREATE OR REPLACE PROCEDURE public.transfer( //这都是函数括号里的内容,指的是这个存储过程函数接受三个参数
IN sender integer, //转出者id,转入者id,金额
IN receiver integer,
IN amount numeric)
AS $BODY$
//用到的中间变量声明。一定要有中间变量,存储原表中改变的值
declare
row_num int:=0; //存储行数
send_user account.username%type; //转出者的姓名,类型是account表中username这个列的数据类型
rec_user account.username%type; //转入者的姓名
begin //转账开始
//更新余额表中 发送者的账户余额 1、更新发送者余额
update accounts
set balance = balance-amount
where id = sender and balance>=amount and amount>0;
GET DIAGNOSTICS row_num:=ROW_COUNT; //用来获取表中受改变的数据的行数
if row_num =0 then //如没有行数受改变,则没找到符合要求的,即表明余额不足或id错误
RAISE NOTICE '账户余额不足,或账户错误';
rollback; //发通知并回滚事务
else //符合标准才进行下一步
//更新余额表中 转入者的账户余额 2、更新转入者余额
update account
set balance = balance+amount
where id =receiver;
GET DIAGNOSTICS row_num:=ROW_CONUT; //获取受改变的行数
if row_num=0 then
RAISE NOTICE '账户错误,转账不成功';
rollback;
else //符合标准才进行下一步
3、从原表中挑出转出者和转入者的姓名赋给中间变量,向转账信息表中添加数据。一定要有中间变量,不然挑出来再放进去太麻烦
select username into send_user from account where id= sender;
select username into rec_user from accounts where id = receive;
insert into translists(sendid,sendname,recid,recname,transdate,transamount)
values(sender,send_user,receiver,rec_user,now(),amount);
GET DIAGNOSTICS row_num:= ROW_COUNT; //获取改变的行数
if row_num =0 then
RAISE NOTICE '转账登记出错,转账不成功' ;
rollback;
else //符合要求进入下一步 4、发出提示信息,转账成功
RAISE NOTICE '%向%成功转款%元' , send_user,rec_user,amount;
commit; //提交
end if; //有三步用来判断失败情况,所以结束三个if
end if;
end if;
end;
$BODY$
触发器—两个表,一个函数(返回值为触发器类型),一个触发器
在汽⻋租赁销售管理系统中有如下表
◦ Car(CarID,loanprice,status)//status取值为“租赁”、“归还”
◦ Stock(ID,CarID,stocknum)//stocknum库存数量
编写insert触发器的触发器函数stockchange(),实现car表新增时⾃动增减库存
注意:直接用NEW代替最新的表格
注意WHERE的条件,一般都是让要改的表里的值=NEW的值
$前面有AS别忘了
注意:UPDATE 表名 后面跟SET
别忘了END IF 重要的是,ELSIF
最后要有返回类型NEW LOD
CREATE OR REPLACE FUNCTION stockchange()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = '租赁' THEN -- 判断操作类型和状态字段值
UPDATE Stock
SET stocknum = stocknum - 1
WHERE CarID = NEW.CarID;
ELSIF NEW.status = '归还' THEN
UPDATE Stock
SET stocknum = stocknum + 1
WHERE CarID = NEW.CarID;
END IF;
RETURN NEW;END;
$$ LANGUAGE plpgsql;
触发器:用来给新建的变化的表格添加内容
第一步:创建表
必须有两个表,触发器是依附于一个表的。比如修改一个表的数据,修改就会触发,而这个变化会存储于另一个表里。
两个主键的必须要有约束
CREATE TABLE student_score
(
sid char(10) NOT NULL,
cid char(10) NOT NULL,
score numeric(5,1) NULL,
CONSTRAINT student_score_PK PRIMARY KEY(sid,cid)
);
新的表用来记录变化。
- 用户名
- 不变的学号和姓名(这个可能也会变,但是删除的时候都存的旧的,插入的时候都存的新的,更新的时候只存旧的,因为条件是新学号姓名=旧学号姓名)
- 更新时间
- 旧的要修改的内容
- 新的要修改的内容
CREATE TABLE audit_score
( username character(20) , --用户名
sid character(10) ,
cid character(10) ,
updatetime timestamp(0) , --修改时的时间
oldscore numeric(5,1), --修改前的成绩
newscore numeric(5,1) --修改后的成绩
)
timestamp(0)的数据类型是2024-05-17 10:30:45 精确到秒,0代表不要精确到秒后面的毫秒等等小数点
numeric(5,1)
- 总长度 (5):表示数字的总位数,包括整数部分和小数部分。
- 小数位数 (1):表示小数部分的位数。
第二步:创建触发器函数:函数声明时返回值是trigger,函数结束时返回值是null,注意函数内部每个if返回值new或old
函数声明 函数体包围$$ begin和end language plpgsql 这个4个别忘
Insert into 后面如果已经有了中间变量的值,就直接values(放中间变量)
没有就要 后面加 SELECE
要有返回值NEW 或OLD
CREATE OR REPLACE FUNCTION score_audit()
RETURNS TRIGGER AS $score_aidit$
BEGIN
IF(TG_OP = 'DELETE') THEN
INSERT INTO audit_score
SELECT user,old.sid,old.cid,now(),old.score; //删除只存旧的,新的不用管
RETURN OLD; //注意函数内部的返回值
ELSE (TG_OP ='UPDATE') THEN
INSERT INTO audit_score
SELECT user,old.sid,old.cid,now(),old.score,new.score //更新旧的新的都有
WHERE old.sid=new.sid and old.cid=new.cid; //这个是用户学号姓名不变的情况下
RETURN NEW;
ELSIF(TG_OP = 'INSERT') THEN
INSERT INTO audit_score
SELECT user,new.sid,new.cid,now(),null,new.score; //插入旧的为null,再写新的
RETURN NEW;
END IF;
RETURN NULL;
END;
$score_audit$ LANGUAGE plpgsql;
TG_OP 是一个特殊变量,表示触发器操作类型:INSERT、UPDATE 或 DELETE
- 使用 OLD 访问被删除记录的旧值。
- user 是执行操作的数据库用户。
- now() 获取当前时间。
- RETURN OLD; 返回被删除的旧记录。
- NEW 访问新值。
- RETURN NEW; 返回更新后的新记录。
- 触发器函数的结束,返回 NULL 表示操作完成。
第三步:创建触发器
CREATE TRIGGER score_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON student_score
FOR EACH ROW EXECUTE PROCEDURE scour_audit();
执行
游标(一个函数,里面使用了游标的语法,返回类型boolean/viod,有中间变量要declare)
游标:用来存储一段查询结果
游标通常在函数,过程,触发器里面使用
函数声明 函数体包围 变量声明 begin和end 语言plpgsql 这五个别忘
不带参数的游标查询student表中的学号,姓名,性别
如果题目中有输出的话别忘了输出内容
//在函数中使用游标
CREATE OR REPLACE FUNCTION cursorDemo()
RETURN BOOLEAN AS $BODY$
declare
//首先要声明游标变量,其次声明要输出的变量名,因为要把从游标里得到的数据存放到这里面输出
unbound_refcursor refcursor;
vsid varchar;
vsname varchar;
vsgender varchar;
BEGIN
//打开游标并给游标绑定查询语句。也就是先把要的东西存到游标里
OPEN unbond_refcursor FOR EXECUTE
'select sid,sname,sex
from student';
//开始把游标里的东西给之前定义的变量
LOOP
FETCH unbound_refcursor INTO vsid,vsname,vsgender;
IF FOUND THEN //检查游标中是否取到数据,检查到了就输出
raise notice '%,%,%',vsid,vsname,vsgender;
ELSE
exit;
END IF;
end loop;
close unbound_refcursor;
raise notice '取循环结束';
return true; //要有最后的返回结果
//必要的异常判断
exception when others then raise except 'error(%)',sqlerrm;
end;
$BODY$ LANGUAGE plpgsql;
从成绩表中查询分数大于某给定值的学号和课程号
成绩表(学号,课程号,成绩)
%ROWTYPE 是一种属性,它为变量定义一个记录类型,该类型的结构与数据库表或视图中的某一行的结构相同。它包括所有列的类型和顺序。使用
table_name%ROWTYPE 可以创建一个包含与表的列对应的字段的记录变量。
也就是vstuscore 这个变量的数据类型是stu_score这个表中的一行的数据类型(也就是好几列),所以这个变量能存储好几列,也就是直接存储一行
另一种等效的方法就是类似于上面,定义好几个变量,也就是把列分开定义
CREATE OR REPLACE FUNCTION cursorDemo2(myscore int)
RETURN void AS $BODY$
declare
vstuscore stu_score%ROWTYPE;
vstucursor cursor(invalue int) FOR //输入的参数是分数,类型是int
SELECT sid,cid,score
FROM stu_score
WHRER score>=invalue
ORDER BY sid;
BEGIN
OPEN vstucursor(myscore); //外部传入的参数,既是函数的传入值,又是游标的传入值
LOOP
FETCH vstucursor INTO vstuscore;
EXIT WHEN NOT FOUND;
RAISE NOTICE '%,%,%',vstusore.sid,vstusore.cid,vstusore.score;
END LOOP;
CLOSE vstucursor;
END;
$BODY$ LANGUAGE plpgsql;
使用:
SELECT cursorDemo2(85);