原文筆記

本課程筆記資料庫系統 · 30 篇
数据库补充习题 70+100 复习索引cs/db/100+70/00-复习索引Day 01 - 数据库100题:单项选择 1-20cs/db/100+70/Day01-数据库100题-选择01-20Day 02 - 数据库100题:单项选择 21-40cs/db/100+70/Day02-数据库100题-选择21-40Day 03 - 数据库100题:单选 41-50,填空 51-60cs/db/100+70/Day03-数据库100题-选择41-50填空51-60Day 04 - 数据库100题:填空 61-80cs/db/100+70/Day04-数据库100题-填空61-80Day 05 - 数据库100题:填空 81-100cs/db/100+70/Day05-数据库100题-填空81-100Day 06 - 数据库70题:试题 1-11cs/db/100+70/Day06-数据库70题-试题01-11Day 07 - 数据库70题:试题 12-25cs/db/100+70/Day07-数据库70题-试题12-25Day 08 - 数据库70题:综合题 1-8cs/db/100+70/Day08-数据库70题-综合01-08Day 09 - 数据库70题:综合题 22-33cs/db/100+70/Day09-数据库70题-综合22-33Day 10 - 数据库70题:综合题 37-47cs/db/100+70/Day10-数据库70题-综合37-47Day 11 - 数据库70题:综合题 48-61cs/db/100+70/Day11-数据库70题-综合48-61Day 12 - 数据库70题:综合题 62-71cs/db/100+70/Day12-数据库70题-综合62-71基础概念与题型全文cs/db/notes/01-基础概念与题型全文SQL语句与数据库操作全文cs/db/notes/02-SQL语句与数据库操作全文数据库编程全文cs/db/notes/03-数据库编程全文数据库设计与规范化全文cs/db/notes/04-数据库设计与规范化全文查询优化、恢复、并发控制全文cs/db/notes/05-查询优化恢复并发控制全文数据库规范化流程cs/db/notes/normalization2025-2026-2 计科数据库系统期末cs/db/pastpapers/25-26cs2024-2025-2 计科数据库系统期末cs/db/pastpapers/db-pastpaper-1数据库系统完整讲义总览与考试题型地图cs/db/tutorial/00-总览与考试题型地图01 数据库基础与关系模型cs/db/tutorial/01-数据库基础与关系模型02 SQL 与关系代数cs/db/tutorial/02-SQL与关系代数03 数据库编程专项:存储过程、触发器、游标、嵌入式 SQLcs/db/tutorial/03-数据库编程专项04 数据库设计、E-R 图与规范化cs/db/tutorial/04-数据库设计ER与规范化05 查询优化专项:关系代数、查询树、优化树cs/db/tutorial/05-查询优化专项06 事务、并发控制与数据库恢复cs/db/tutorial/06-事务并发控制与恢复07 填空选择速记清单cs/db/tutorial/07-填空选择速记清单08 大题答题模板cs/db/tutorial/08-大题答题模板

03 数据库编程专项:存储过程、触发器、游标、嵌入式 SQL

数据库编程题是本课程大题高频点。new-pic 和历年题都出现过:带参数存储过程、输出参数、游标、触发器、嵌入式 SQL 填空、事务错误处理。

一、存储过程总思路

存储过程是一组预先编译保存在数据库中的 SQL 语句和流程控制语句。它适合完成经常调用、逻辑较复杂、需要参数的数据库操作。

答题结构:

  1. 如有要求,先删除同名过程。
  2. CREATE PROCEDURE 声明过程名和参数。
  3. 声明局部变量。
  4. 写核心 SQL。
  5. 按题意处理不存在、NULL、库存不足等情况。
  6. 必要时写调用验证。

二、不带参数与带参数存储过程

不带参数

CREATE PROCEDURE ListAllStudents
AS
BEGIN
    SELECT Sno, Sname, Sdept
    FROM Student;
END;
GO

EXEC ListAllStudents;

带输入参数

CREATE PROCEDURE ListStudentsByDept
    @dept varchar(20)
AS
BEGIN
    SELECT Sno, Sname, Sdept
    FROM Student
    WHERE Sdept = @dept;
END;
GO

EXEC ListStudentsByDept @dept = N'计算机科学与技术';

带输出参数

题型:Employees(emp_id, emp_name, dept_id, salary),输入员工号,输出员工姓名;若不存在或输入为 NULL,输出“无此员工”。

SQL Server 写法:

CREATE PROCEDURE proc_get_employee_name
    @in_emp_id int,
    @out_emp_name varchar(50) OUTPUT
AS
BEGIN
    IF @in_emp_id IS NULL
    BEGIN
        SET @out_emp_name = N'无此员工';
        RETURN;
    END;

    SELECT @out_emp_name = emp_name
    FROM Employees
    WHERE emp_id = @in_emp_id;

    IF @out_emp_name IS NULL
        SET @out_emp_name = N'无此员工';
END;
GO

DECLARE @name varchar(50);
EXEC proc_get_employee_name 1001, @name OUTPUT;
SELECT @name AS 员工姓名;

MySQL/通用过程题若明确写 IN、OUT,可写:

CREATE PROCEDURE proc_get_employee_name(
    IN in_emp_id INT,
    OUT out_emp_name VARCHAR(50)
)
BEGIN
    IF in_emp_id IS NULL THEN
        SET out_emp_name = '无此员工';
    ELSE
        SELECT emp_name
        INTO out_emp_name
        FROM Employees
        WHERE emp_id = in_emp_id;

        IF out_emp_name IS NULL THEN
            SET out_emp_name = '无此员工';
        END IF;
    END IF;
END;

考试若没有指定 DBMS,本课程课堂多按 SQL Server/T-SQL 写。

三、库存出库/入库存储过程

出库题模板

题型:BOOK(BID, BNAME, QTY),出库时若库存足够则扣减并通知余量,否则提示库存不足。参数:书号 @bid、出库数量 @s。

CREATE PROCEDURE BookOut
    @bid char(10),
    @s int
AS
BEGIN
    DECLARE @qty int;

    SELECT @qty = QTY
    FROM BOOK
    WHERE BID = @bid;

    IF @qty IS NULL
    BEGIN
        PRINT N'无此图书';
        RETURN;
    END;

    IF @qty >= @s
    BEGIN
        UPDATE BOOK
        SET QTY = QTY - @s
        WHERE BID = @bid;

        SELECT BID, BNAME, QTY AS 库存余量
        FROM BOOK
        WHERE BID = @bid;
    END
    ELSE
    BEGIN
        PRINT N'库存不足';
    END
END;
GO

EXEC BookOut @bid = 'B001', @s = 10;

入库题模板

若存在则增加库存;不存在则插入新书,书名暂为空,并提示“新书入库”。

CREATE PROCEDURE BookIn
    @bid char(10),
    @s int
AS
BEGIN
    IF EXISTS (SELECT 1 FROM BOOK WHERE BID = @bid)
    BEGIN
        UPDATE BOOK
        SET QTY = QTY + @s
        WHERE BID = @bid;

        SELECT BID, BNAME, QTY AS 最新库存
        FROM BOOK
        WHERE BID = @bid;
    END
    ELSE
    BEGIN
        INSERT INTO BOOK(BID, BNAME, QTY)
        VALUES (@bid, NULL, @s);
        PRINT N'新书入库';
    END
END;
GO

四、游标

游标用于协调 SQL 的集合处理方式与宿主语言/过程语言的单记录处理方式。选择填空常问:游标是用于逐行处理查询结果集的机制。

SQL Server 游标步骤:

  1. 定义游标。
  2. 打开游标。
  3. 读取/推进游标。
  4. 循环处理。
  5. 关闭游标。
  6. 释放游标。

模板:

DECLARE Cur1 CURSOR
FOR
    SELECT p.pno, p.pname, d.dname, d.mana
    FROM Pe AS p
    JOIN Dept AS d ON p.dptno = d.dptno
    WHERE p.pname LIKE N'_武%';

OPEN Cur1;

DECLARE @pno varchar(10),
        @pname varchar(20),
        @dname varchar(20),
        @mana varchar(20);

FETCH NEXT FROM Cur1
INTO @pno, @pname, @dname, @mana;

WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT 职工号 = @pno,
           姓名 = @pname,
           部门 = @dname,
           负责人 = @mana;

    FETCH NEXT FROM Cur1
    INTO @pno, @pname, @dname, @mana;
END;

CLOSE Cur1;
DEALLOCATE Cur1;

注意:

  • FETCH NEXT FROM 游标 INTO 变量列表 的变量数量和查询列数量必须一致。
  • 循环条件是 @@FETCH_STATUS = 0。
  • 关闭后还要 DEALLOCATE 释放。

五、带参数存储过程加游标

题型:查询姓名第二个字为“武”的员工的部门名和负责人,并以标签格式输出。

IF EXISTS (
    SELECT name
    FROM sysobjects
    WHERE name = 'Sele_staff'
      AND type = 'P'
)
DROP PROCEDURE Sele_staff;
GO

CREATE PROCEDURE Sele_staff
    @second_char nchar(1)
AS
BEGIN
    DECLARE Cur1 CURSOR
    FOR
        SELECT p.pno, p.pname, d.dname, d.mana
        FROM Pe AS p
        JOIN Dept AS d ON p.dptno = d.dptno
        WHERE p.pname LIKE N'_' + @second_char + N'%';

    OPEN Cur1;

    DECLARE @pno varchar(10),
            @pname nvarchar(20),
            @dname nvarchar(20),
            @mana nvarchar(20);

    FETCH NEXT FROM Cur1 INTO @pno, @pname, @dname, @mana;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        SELECT 职工号 = @pno,
               姓名 = @pname,
               部门 = @dname,
               负责人 = @mana;

        FETCH NEXT FROM Cur1 INTO @pno, @pname, @dname, @mana;
    END;

    CLOSE Cur1;
    DEALLOCATE Cur1;
END;
GO

EXEC Sele_staff N'武';

六、触发器

触发器是一种特殊存储过程,由事件自动触发,不能通过普通 EXEC 调用。它常用于复杂完整性约束、审计、联动维护。

触发器通常“不带用户手工传入的参数”。它不是像存储过程那样 EXEC proc @x=... 调用,而是在表发生 INSERT/UPDATE/DELETE 时由 DBMS 自动执行。触发器要获取本次被修改的数据,主要靠 inserted 和 deleted 这两张逻辑表。

常见触发时机:

  • AFTER INSERT
  • AFTER UPDATE
  • AFTER DELETE
  • INSTEAD OF INSERT/UPDATE/DELETE

SQL Server 触发器中常用临时表:

  • inserted:插入后的新行;更新时也保存更新后的新行。
  • deleted:删除前的旧行;更新时也保存更新前的旧行。

营业时间触发器

题型:SaleDetail 只能在工作日 9:00-17:00 修改。

CREATE TRIGGER trg_SaleDetail_business_hours
ON SaleDetail
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    DECLARE @hour int, @weekday int;

    SELECT @hour = DATEPART(hh, GETDATE());
    SELECT @weekday = DATEPART(weekday, GETDATE());

    IF NOT (@hour BETWEEN 9 AND 17 AND @weekday IN (2, 3, 4, 5, 6))
    BEGIN
        PRINT N'Not business hours!';
        ROLLBACK;
    END;
END;
GO

DATEPART(weekday, GETDATE()) 的结果受 DATEFIRST 影响。课堂题一般默认周一到周五为 2-6。若想更稳,可先 SET DATEFIRST 7,但考场通常不用展开。

七、事务与错误处理

多步删除/修改必须使用事务,保证全部成功才提交,任何一步失败要回滚。

例:删除学生时先删除成绩,再删除学生。

CREATE PROCEDURE DeleteStudent
    @sno char(9)
AS
BEGIN
    BEGIN TRANSACTION;

    DELETE FROM Grade WHERE Sno = @sno;
    IF @@ERROR <> 0
    BEGIN
        ROLLBACK TRANSACTION;
        PRINT N'删除成绩失败,事务已回滚';
        RETURN;
    END;

    DELETE FROM Student WHERE Sno = @sno;
    IF @@ERROR <> 0
    BEGIN
        ROLLBACK TRANSACTION;
        PRINT N'删除学生失败,事务已回滚';
        RETURN;
    END;

    COMMIT TRANSACTION;
    PRINT N'学生删除成功,事务已提交';
END;
GO

@@ERROR 的规则:

  • 每条语句执行后必须立刻判断。
  • 不能等多条语句执行完再判断,因为会被后续语句覆盖。
  • 出错必须回滚。
  • 出错必须退出过程,避免继续执行破坏一致性。

八、嵌入式 SQL

嵌入式 SQL 是把 SQL 语句嵌入 C 等宿主语言中使用。它解决的问题是:SQL 一次处理一组记录,宿主语言通常一次处理一条记录,两者通过主变量、SQLCA、游标协调。

核心术语:

  • 主变量:宿主语言变量,SQL 中使用时前面加冒号,如 :deptname。
  • SQLCA:SQL 通信区,保存 SQL 执行状态,如 sqlca.sqlcode。
  • 游标:逐行处理查询结果集。

典型填空:

EXEC SQL INCLUDE SQLCA;        /* 定义 SQL 通信区 */

EXEC SQL DECLARE SX CURSOR FOR
SELECT Sno, Sname, Ssex, Sage
FROM Student
WHERE Sdept = :deptname;

EXEC SQL OPEN SX;

EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex, :HSage;

EXEC SQL UPDATE Student
SET Sage = :NEWAGE
WHERE CURRENT OF SX;

完整流程:

  1. EXEC SQL BEGIN DECLARE SECTION; 和 END DECLARE SECTION; 声明主变量。
  2. EXEC SQL INCLUDE SQLCA; 引入通信区。
  3. EXEC SQL CONNECT ... 连接数据库。
  4. DECLARE cursor CURSOR FOR SELECT ... 定义游标。
  5. OPEN cursor 打开游标。
  6. 循环 FETCH cursor INTO :变量。
  7. 判断 sqlca.sqlcode != 0 退出。
  8. 如需更新当前行,用 WHERE CURRENT OF cursor。
  9. CLOSE cursor,COMMIT WORK,DISCONNECT。