数据库_练习

关系数据理论练习:

1、属性集闭包计算:讲义第4章Page71【例7-54】,在纸上独立完成运算,拍照上传。 alt text 2、求最小覆盖:讲义第4章Page93【例1】,在纸上独立完成运算,拍照上传。 alt text 3、模式分解:讲义第4章Page132【例4-4】,在纸上独立完成运算,拍照上传。 alt text

数据定义练习:

某医院住院部信息系统中由病人表R(住院号,姓名,性别,科室号,病房,家庭住址),“住院号”唯一标识表R中的每一个元组,“性别”的取值只能为M或F,“家庭住址”包括省、市、街道、邮编,要求科室号参照科室关系D中的科室号;科室关系D(科室号,科室名,负责人,联系电话),“科室号”唯一标识关系D中的每一个元组。

a. 创建关系R的SQL语句如下:

1
2
3
4
5
6
7
8
CREATE TABLE R (
    住院号 CHAR(8) ________,
    姓名 CHAR(10),
    性别 CHAR(1) ________,
    科室号 CHAR(4),
    家庭住址 ADDR, //ADDR为用户定义的类
    ____________
);

b. 表R中复合属性是__________。(15年第40~43题) 问题1: A. PRIMARY KEY B. REFERENCES D(科室号) C. NOT NULL D. REFERENCES D(科室名)

问题2: A. IN(M,F) B. CHECK(‘M’,‘F’) C. LIKE(‘M’,‘F’) D. CHECK(性别 IN(‘M’,‘F’))

问题3: A. PRIMARY KEY(科室号)NOT NULL UNIQUE B. PRIMARY KEY(科室名)UNIQUE C. FOREIGN KEY(科室号)REFERENCES D(科室号) D. FOREIGN KEY(科室号)REFERENCES D(科室名)

问题4: A. 住院号 B. 姓名 C. 病房 D. 家庭住址

问题1: A
问题2: D
问题3: C
问题4: D

查询练习一:

给定一个部门表Departments,包含字段DepartmentID(部门ID)和DepartmentName(部门名称);一个员工表Employees,包含字段EmployeeID(员工ID)、LastName(姓)、FirstName(名)、Salary(薪资)、Departmen(部门ID),其中Departmen是外键,参照部门表的主键DepartmentID。写出以下需求的SQL语句,上传SQL语句和运行结果图。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 创建部门表
CREATE TABLE 部门表 (
    部门ID INT PRIMARY KEY,
    部门名称 NVARCHAR(50) NOT NULL
);

-- 插入部门数据
INSERT INTO 部门表 (部门ID, 部门名称) VALUES
(1, N'销售部'),
(2, N'市场部'),
(3, N'研发部'),
(4, N'人力资源部');

-- 创建员工表
CREATE TABLE 员工表 (
    员工ID INT PRIMARY KEY,
    姓 NVARCHAR(50) NOT NULL,
    名 NVARCHAR(50) NOT NULL,
    薪资 DECIMAL(10, 2) NOT NULL,
    部门ID INT,
    FOREIGN KEY (部门ID) REFERENCES 部门表(部门ID)
);

-- 插入员工数据
INSERT INTO 员工表 (员工ID, 姓, 名, 薪资, 部门ID) VALUES
(101, N'张', N'三', 6000.00, 1),
(102, N'李', N'四', 4500.00, 1),
(103, N'王', N'五', 7000.00, 2),
(104, N'赵', N'六', 5500.00, 2),
(105, N'钱', N'七', 8000.00, 3),
(106, N'孙', N'八', 5000.00, 3),
(107, N'周', N'九', 4000.00, 4),
(108, N'吴', N'十', 6500.00, 1);

1、编写一个SQL查询,列出所有月薪超过5000的员工的姓名和薪资。

1
2
3
SELECT 姓, 名, 薪资
FROM 员工表
WHERE 薪资 > 5000;

2、编写一个SQL查询,用以计算每个部门的平均薪资。

1
2
3
4
SELECT D.部门名称, AVG(E.薪资) AS 平均薪资
FROM 部门表 AS D
JOIN 员工表 AS E ON D.部门ID = E.部门ID
GROUP BY D.部门名称;

3、编写一个SQL查询,找出薪资高于所在部门平均薪资的所有员工的姓名。

1
2
3
4
SELECT E.姓, E.名, E.薪资, D.部门名称
FROM 员工表 AS E
JOIN 部门表 AS D ON E.部门ID = D.部门ID
WHERE E.薪资 > (SELECT AVG(薪资) FROM 员工表 WHERE 部门ID = E.部门ID);

4、编写一个SQL命令,将所有员工的薪资提高10%,但只限于那些部门平均薪资低于6000的部门。

1
2
3
4
5
6
7
8
UPDATE 员工表
SET 薪资 = 薪资 * 1.10
WHERE 部门ID IN (
    SELECT 部门ID
    FROM 员工表
    GROUP BY 部门ID
    HAVING AVG(薪资) < 6000
);

5、编写一个SQL查询,列出所有部门以及各部门的员工人数。

1
2
3
4
SELECT D.部门名称, COUNT(E.员工ID) AS 员工人数
FROM 部门表 AS D
LEFT JOIN 员工表 AS E ON D.部门ID = E.部门ID
GROUP BY D.部门名称;

查询练习二:

设教学数据库 Education 有三个关系

  • 学生关系 S(SNO,SNAME,AGE,SEX,SDEPT)

  • 学习关系 SC(SNO,CNO,GRADE)

  • 课程关系 C(CNO,CNAME,CDEPT,TNAME)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
-- 创建学生表
CREATE TABLE 学生表 (
    学号 CHAR(10) PRIMARY KEY,
    姓名 NVARCHAR(50) NOT NULL,
    年龄 INT,
    性别 CHAR(1),
    系别 NVARCHAR(50)
);

-- 插入学生数据
INSERT INTO 学生表 (学号, 姓名, 年龄, 性别, 系别) VALUES
('S001', N'小明', 20, 'M', N'计算机系'),
('S002', N'小红', 21, 'F', N'软件工程系'),
('S003', N'小刚', 19, 'M', N'计算机系'),
('S004', N'小丽', 22, 'F', N'自动化系'),
('S005', N'小强', 20, 'M', N'计算机系');

-- 创建课程表
CREATE TABLE 课程表 (
    课程号 CHAR(10) PRIMARY KEY,
    课程名 NVARCHAR(50) NOT NULL,
    开课系 NVARCHAR(50),
    教师名 NVARCHAR(50)
);

-- 插入课程数据
INSERT INTO 课程表 (课程号, 课程名, 开课系, 教师名) VALUES
('C1', N'数据库原理', N'计算机系', N'王老师'),
('C2', N'数据结构', N'计算机系', N'李老师'),
('C3', N'操作系统', N'软件工程系', N'张老师'),
('C4', N'高等数学', N'数学系', N'赵老师'),
('C5', N'DS', N'计算机系', N'陈老师');

-- 创建学习表
CREATE TABLE 学习表 (
    学号 CHAR(10),
    课程号 CHAR(10),
    成绩 INT,
    PRIMARY KEY (学号, 课程号),
    FOREIGN KEY (学号) REFERENCES 学生表(学号),
    FOREIGN KEY (课程号) REFERENCES 课程表(课程号)
);

-- 插入学习数据
INSERT INTO 学习表 (学号, 课程号, 成绩) VALUES
('S001', 'C1', 85),
('S001', 'C2', 90),
('S002', 'C1', 78),
('S002', 'C3', 88),
('S003', 'C2', 75),
('S003', 'C4', 80),
('S004', 'C1', 92),
('S004', 'C3', 85),
('S005', 'C2', 88),
('S005', 'C4', 95),
('S001', 'C5', 80); -- S001 选修了 'DS'

写出以下需求的SQL语句,上传SQL语句和运行结果图。

1、检索学习课程号为 C2 的学生学号与姓名;

1
2
3
4
SELECT S.学号, S.姓名
FROM 学生表 AS S
JOIN 学习表 AS SC ON S.学号 = SC.学号
WHERE SC.课程号 = 'C2';

2、检索选修课程名为“DS”的学生学号与姓名;

1
2
3
4
5
SELECT S.学号, S.姓名
FROM 学生表 AS S
JOIN 学习表 AS SC ON S.学号 = SC.学号
JOIN 课程表 AS C ON SC.课程号 = C.课程号
WHERE C.课程名 = 'DS';

3、检索至少选修课程号为 C2 和 C4 的学生学号。

1
2
3
4
5
6
7
SELECT 学号
FROM 学习表
WHERE 课程号 = 'C2'
INTERSECT
SELECT 学号
FROM 学习表
WHERE 课程号 = 'C4';

存储过程和触发器练习:

某企业网上书城系统的部分关系模式如下:

书籍信息表:books(book_no, book_name, press_no, ISBN, price, sale_type, all_nums),其中属性含义分别为:书籍编码、书籍名称、出版商编码、ISBN、销售价格、销售分类、当前库存数量;
书籍销售订单表:orders(order_no, book_no, book_nums, book_price, order_date, amount),其中属性分别为:订单编码、书籍编码、书籍数量、书籍价格、订单日期和总金额。
书籍再购额度表:booklimit(book_no, sale_type, limit_amount),其中属性含义分别为:书籍编码、销售分类、再购额度;
书籍最低库存表:bookminlevel(book_no, level),其中属性含义分别为:书籍编码、书籍最低库存数量;
书籍采购表:bookorders(book_no, order_amount),其中属性含义分别为:书籍编码和采购数量。
有关关系模式的说明如下:
(1)下划线标出的属性是表的主码。
(2)根据书籍销售情况来确定书籍的销售分类:销售数量小于1万的为普通类型,其值为0;1万及以上的为热销类型,其值为1。
(3)系统具备书籍自动补货功能,涉及到的关系模式有:书籍再购额度表、书籍最低库存表、书籍采购表。其业务逻辑是:当某书籍库存小于其最低库存数量时,根据书籍的销售分类以及书籍再购额度表中的再购额度,生成书籍采购表中的采购订单,完成自动补货操作。
【问题1】
系统定期扫描书籍销售订单表,根据书籍总的销售情况来确定书籍的销售类别。下面是系统中设置某书籍销售类别的存储过程,结束时需显示提交返回。请不全空缺处的代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
CREATE PROCEDURE UpdateBookSaleType (IN bno varchar(20))
DECLARE
    all_nums number(6);
BEGIN
    SELECT ____(a)____ (book_nums) INTO all_nums FROM orders
    WHERE book_no=____(b)____;
    IF all_nums<=____(c)____ THEN
        UPDATE books SET sale_type=0 WHERE book_no=bno;
    ELSE
        UPDATE books SET sale_type=____(d)____ WHERE book_no=bno;
    END IF
END
  • (a) SUM (计算总销售数量)
  • (b) bno (根据传入的书籍编码进行筛选)
  • (c) 10000 (销售数量小于1万为普通类型)
  • (d) 1 (1万及以上为热销类型) 【问题2】 下面是系统中自动补货功能对应的触发器,请补全空缺处的代码。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
CREATE TRIGGER BookOrdersTrigger ____(f)____ update
    of ____ (g)____ on books
    ____(h)____
    WHEN ____(i)____ < (SELECT level FROM bookminlevel
                         WHERE bookminlevel.book_no=OLD.book_no)
    AND ____(j)____ >= (SELECT level FROM bookminlevel
                        WHERE bookminlevel.book_no=OLD.book_no)
BEGIN
    INSERT INTO ____(k)____
    (SELECT book_no, limit_amount
     FROM booklimit as TMP
     WHERE TMP.book_no=OLD.book_no
     AND TMP.sale_type=OLD.sale_type);
END
  • (f) AFTER (在更新操作之后触发)
  • (g) all_nums (当books表的all_nums字段更新时触发)
  • (h) FOR EACH ROW (对于每一行受影响的行都执行触发器)
  • (i) NEW.all_nums (更新后的库存数量)
  • (j) OLD.all_nums (更新前的库存数量)
  • (k) bookorders (插入到书籍采购表)

游标的练习:

1、讲义第8章Page47的【例8-2】,练习游标,上传编写的SQL语句和运行结果截图。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
-- **创建仓库表 (如果不存在,请先运行此段)**
-- 注意:在实际运行中,请确保您有权限创建表,并且数据库已正确选择。
IF OBJECT_ID(N'仓库表', N'U') IS NOT NULL
    DROP TABLE 仓库表;
GO

CREATE TABLE 仓库表 (
    仓库号 VARCHAR(10) PRIMARY KEY,
    所在城市 NVARCHAR(100), -- 使用 NVARCHAR 兼容中文
    仓库面积 INT
);
GO

-- **插入示例数据(根据图中仓库列表提供的数据)**
INSERT INTO 仓库表 (仓库号, 所在城市, 仓库面积) VALUES
('WH1', N'北京', 500),
('WH2', N'上海', 370),
('WH3', N'广州', 300),
('WH4', N'武汉', 400),
('WH5', N'成都', 450),
('WH6', N'北京', 650);
GO

-- **游标操作代码 (与图中提供的示例完全一致)**
-- 从游标wh_cursor读当前记录到变量
DECLARE wh_cursor CURSOR FOR 
    SELECT 仓库号, 所在城市, 仓库面积 FROM 仓库表;

OPEN wh_cursor;

-- 声明变量
DECLARE @whno VARCHAR(10), @city NVARCHAR(100), @area INT;

FETCH NEXT FROM wh_cursor INTO @whno, @city, @area;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @whno + ' ' + @city + ' ' + STR(@area, 4);
    FETCH NEXT FROM wh_cursor INTO @whno, @city, @area;
END

-- 关闭游标
CLOSE wh_cursor;

-- 释放游标
DEALLOCATE wh_cursor;
GO

并发事务的干扰问题:

系统中有三个事务T1、T2、T3分别对数据R1和R2进行操作,其中R1和R2的初值R1=120、R2=50,假设事务T1、T2、T3操作的情况如下图所示,图中T1和T2间并发操作( )问题,T2与T3间并发操作( )问题。(12年第45~46题)

时间 T1 T2 T3
t1 Read(R1);
t2 Read(R2);
t3 X = R1 + R2;
t4 Read(R1);
t5 Read(R2);
t6 Read(R2);
t8 Write(R2);
t9 Read(R1);
t10 Read(R2);
t11 X = R1 + R2;
t12 演算X R2 = R2 + 80;
t13 Write(R2);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
问题1: B
T1在t1和t2读取R1和R2,计算X。
T2在t4-t8修改了R2并写入。
T1在t9和t10再次读取R1和R2。由于T2的修改,T1第二次读R2时,R2的值已经改变(R2=R1+R2),导致T1两次读取R2的结果不一致,这是不可重复读问题。
问题2: C
T2在t5读取R2 (初始值50)。  
T3在t6读取R2 (初始值50)。  
T2在t7计算R2=R1+R2 (120+50=170),并在t8写入R2=170。  
T3在t12计算R2=R2+80 (50+80=130),并在t13写入R2=130。   
T3的写入覆盖了T2的写入,导致T2的修改丢失。  

隔离级别练习:

事务T1中有两次查询学生表中的男生人数,在这两次查询执行中间,事务T2对学生表中加入了一条男生记录,导致T1两次查询的结果不一致,此类问题属于( ),为解决这一问题,应采用的隔离级别是( )。(14年第54~55题)
问题1:
A. 不可重复读
B. 读脏数据
C. 丢失修改
D. 幻象读
问题2:
A. Read Uncommitted
B. Read Committed
C. Repeatable Read
D. Serializable
问题1: D. 幻象读
问题2: D. Serializable

封锁练习:

若事务T1对数据D1已加排它锁,事务T2对数据D2已加共享锁,那么事务T2对数据D1( );事务T1对数据D2( )。(13年第45~46题)
问题1: A. 加共享锁成功,加排它锁失败
B. 加排它锁成功,加共享锁失败
C. 加共享锁、排它锁都成功
D. 加共享锁、排它锁都失败
问题2:
A. 加共享锁成功,加排它锁失败
B. 加排它锁成功,加共享锁失败
C. 加共享锁、排它锁都成功
D. 加共享锁、排它锁都失败

问题1: D. 加共享锁、排它锁都失败
问题2: C. 加共享锁、排它锁都成功

数据库备份与恢复练习:

A、完整 (10:00)→日志 (11:00)→日志 (13:00)→日志 (15:00)→差异 (16:00)

  1. 准备工作:创建数据库和表
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-- 使用 master 数据库
USE master;
GO

-- 如果数据库已存在,先删除它以便重新开始
IF DB_ID(N'学生管理数据库') IS NOT NULL
    DROP DATABASE 学生管理数据库;
GO

-- 创建新的数据库
CREATE DATABASE 学生管理数据库;
GO

-- 切换到新创建的数据库
USE 学生管理数据库;
GO

-- 创建学生信息表
CREATE TABLE 学生信息 (
    学号 INT PRIMARY KEY IDENTITY(1,1),
    姓氏 NVARCHAR(50) NOT NULL,
    名字 NVARCHAR(50) NOT NULL,
    出生日期 DATE,
    入学日期 DATE DEFAULT GETDATE()
);
GO

-- 插入一些初始学生数据
INSERT INTO 学生信息 (姓氏, 名字, 出生日期) VALUES
(N'张', N'三', '2000-01-15'),
(N'李', N'四', '2001-03-20'),
(N'王', N'五', '1999-07-10');
GO

-- 查看当前学生数据
SELECT * FROM 学生信息;
GO
  1. 进行完整备份 (Full Backup) 假设这是在 10:00 进行的。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
USE master; -- 备份操作通常在 master 数据库中执行
GO

-- 执行完整备份
BACKUP DATABASE 学生管理数据库
TO DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_完整_20250527_100000.bak' -- **已更新路径**
WITH NOFORMAT, NOINIT, NAME = N'学生管理数据库完整备份_10点', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
GO
PRINT '完整备份完成,时间:10:00';
GO
  1. 插入一些新数据 (模拟数据库变化)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
USE 学生管理数据库;
GO

INSERT INTO 学生信息 (姓氏, 名字, 出生日期) VALUES
(N'赵', N'六', '2002-05-25');
GO

SELECT * FROM 学生信息; -- 确认新数据
GO
PRINT '10:30 插入新数据完成';
GO
  1. 进行第一次事务日志备份 (Log Backup)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
USE master;
GO

-- 执行第一次事务日志备份
BACKUP LOG 学生管理数据库
TO DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_日志_20250527_110000.trn' -- **已更新路径**
WITH NOFORMAT, NOINIT, NAME = N'学生管理数据库日志备份_11点', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
GO
PRINT '日志备份完成,时间:11:00';
GO
  1. 再次插入一些新数据 (模拟更多数据库变化)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
USE 学生管理数据库;
GO

INSERT INTO 学生信息 (姓氏, 名字, 出生日期) VALUES
(N'钱', N'七', '2003-09-01');
GO

SELECT * FROM 学生信息; -- 确认新数据
GO
PRINT '12:00 插入新数据完成';
GO
  1. 进行第二次事务日志备份 (Log Backup)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
USE master;
GO

-- 执行第二次事务日志备份
BACKUP LOG 学生管理数据库
TO DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_日志_20250527_130000.trn' -- **已更新路径**
WITH NOFORMAT, NOINIT, NAME = N'学生管理数据库日志备份_13点', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
GO
PRINT '日志备份完成,时间:13:00';
GO
  1. 进行差异备份 (Differential Backup)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
USE master;
GO

-- 执行差异备份
BACKUP DATABASE 学生管理数据库
TO DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_差异_20250527_160000.bak' -- **已更新路径**
WITH DIFFERENTIAL, NOFORMAT, NOINIT, NAME = N'学生管理数据库差异备份_16点', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
GO
PRINT '差异备份完成,时间:16:00';
GO
  1. 进行破坏性操作 (模拟数据丢失或损坏)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
USE 学生管理数据库;
GO

TRUNCATE TABLE 学生信息; -- 清空表
GO

SELECT * FROM 学生信息; -- 确认表已空
GO

PRINT '已清空学生信息表,模拟数据丢失。';
GO
  1. Q1 恢复到 16:00
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
USE master;
GO

-- 确保数据库处于离线或删除状态,才能进行恢复
-- 在这里,我们通常会先尝试将数据库置为离线,如果失败(例如数据库不存在),则跳过
IF DB_ID(N'学生管理数据库') IS NOT NULL
BEGIN
    ALTER DATABASE 学生管理数据库 SET OFFLINE WITH ROLLBACK IMMEDIATE;
    PRINT '数据库 学生管理数据库 已置为离线。';
END;
-- 或者如果您之前直接删除了数据库,则无需以上 ALTER DATABASE 语句。

-- 1. 恢复完整备份 (10:00)
RESTORE DATABASE 学生管理数据库
FROM DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_完整_20250527_100000.bak'
WITH NORECOVERY,
REPLACE, -- **新增:强制覆盖现有数据库**
MOVE N'学生管理数据库' TO N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\DATA\学生管理数据库.mdf',
MOVE N'学生管理数据库_log' TO N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\DATA\学生管理数据库_log.ldf';
GO

PRINT '完整备份恢复完成 (NORECOVERY)。';

-- 2. 恢复差异备份 (16:00)
RESTORE DATABASE 学生管理数据库
FROM DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_差异_20250527_160000.bak'
WITH RECOVERY;
GO

PRINT '差异备份恢复完成 (RECOVERY),数据库已联机到 16:00 的状态。';

-- 验证恢复结果
USE 学生管理数据库;
GO
SELECT * FROM 学生信息;
GO
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
USE master;
GO

-- 再次模拟数据库被破坏或清空,以便重新进行恢复
IF DB_ID(N'学生管理数据库') IS NOT NULL
BEGIN
    ALTER DATABASE 学生管理数据库 SET OFFLINE WITH ROLLBACK IMMEDIATE;
    PRINT '数据库 学生管理数据库 已置为离线。';
END;

-- 1. 恢复完整备份 (10:00)
RESTORE DATABASE 学生管理数据库
FROM DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_完整_20250527_100000.bak'
WITH NORECOVERY,
REPLACE, -- **新增:强制覆盖现有数据库**
MOVE N'学生管理数据库' TO N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\DATA\学生管理数据库.mdf',
MOVE N'学生管理数据库_log' TO N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\DATA\学生管理数据库_log.ldf';
GO

PRINT '完整备份恢复完成 (NORECOVERY)。';

-- 2. 恢复 11:00 的日志备份
RESTORE LOG 学生管理数据库
FROM DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_日志_20250527_110000.trn'
WITH NORECOVERY;
GO

PRINT '日志备份 11:00 恢复完成 (NORECOVERY)。';

-- 3. 恢复 13:00 的日志备份,停止在 12:00
RESTORE LOG 学生管理数据库
FROM DISK = N'D:\sql\gen\MSSQL16.MSSQLSERVER\MSSQL\Backup\学生管理数据库_日志_20250527_130000.trn'
WITH RECOVERY, STOPAT = '2025-05-27 12:00:00.000';
GO

PRINT '日志备份 13:00 恢复完成并停止在 12:00,数据库已联机。';

-- 验证恢复结果
USE 学生管理数据库;
GO
SELECT * FROM 学生信息;
GO

后续选择略

本文采用 CC BY-NC-SA 4.0 协议授权,转载请注明出处。
May 27, 2025 21:55 +0800
2332ly