《数据库系统概念(第六版)》读书笔记——第6章:高级SQL

高级SQL

标签(空格分隔): 数据库


JDBC

示例(来自课后习题)

public class ResultSetTable {
    private ResultSet resultSet;
    private ResultSetMetaData reslutSetMetaDate;
    private int cols_num;

    public void resultSetTable (ResultSet resultSet) throws SQLException {
        this.resultSet = resultSet;
        reslutSetMetaDate = resultSet.getMetaData();
        cols_num = reslutSetMetaDate.getColumnCount();

        for (int i = 1; i <= cols_num; i++) {
            System.out.println(reslutSetMetaDate.getColumnName(i) + '  ');
        }
        while ( resultSet.next()) {
            for (int i = 1; i  <= cols_num; i++) {
                system.out.println(reslutSet.getString(reslutSetMetaDate.getColumnName(i)) + ' ');}
        }
    }
}

示例二

函数和过程

1. SQL和Java/C++/C都可以编写函数和过程,有什么不同?

SQL

函数和过程允许“业务逻辑”作为存储过程记录在数据库中,并在数据库内执行。尽管这样的“业务逻辑”能被写成程序设计语言过程并完全存储在数据库之外,但把它们定义成数据库的存储过程有以下的优点

- 允许多个应用访问
- 当业务规则发生变化时,进行单个节点的改变,而不必更改应用程序的其他部分
- 应用代码可以调用存储过程,而不是直接更新数据库关系

程序设计语言

SQL标准不被很多数据库所支持,由此,即便是最基本的特性在不同的数据库产品中,都会有不同的语法与语义。在这种情况下,使用一种命令式程序设计语言,从SQL查询和触发器的定义中调用,可以免去程序员学习多种非标准数据库语言的烦恼。

2. 声明和调用SQL函数和过程

示例一:普通函数

--声明函数
--给定系名,返回该系的教师数木
create function dept_count (dept_name varchar(20))
    returns integer
    beign
        declare d_count integer
        select d_count integer
        from instructor
        where instructor.dept_name = dept_name;
        return d_count;
    end
--调用函数
--返回教师数大于12的所有系的名称和预算
select dept_name, budget
from department
where dept_count(dept_name) > 12;

示例二:表函数——返回表作为结果

--声明函数
--返回包含某特定系的所有教师的表
create function instructor_of(dept_name varchar(20))
    returns table(
            ID varchar(5),
            name varchar(20),
            dept_name varchar(20),
            salary numeric(8, 2))
    return table
        (select ID, name, dept_name, salary
         from instructor
         where instructor.dept_name = instrutor_of.dept_name);
--调用
--返回金融系的所有教师
select *
from table(instructor_of('Finance'));

示例三:过程

--改写示例一
create procedure dept_count_proc(in dept_name varchar(20), out d_count integer)
    begin
        select count(*) into d_count
        from instructor
       where instructor.dept_name = dept_count_proc.dept_name;
    end

3. 支持函数和过程的语言构造

- declare :声明变量,可以是任意合法的SQL类型
- set :赋值变量
- begin...end:复合语句
- begin atomic...end:作为一个单一事务执行
- while语句:
    while 布尔表达式 do
          语句序列;
    end while
- repeat语句:
    repeat 
          语句序列;
    until 布尔表达式
    end repeat
- for循环
    declare n integer default 0;
    for r as 
        select budget from department where dept_name = 'Music'
    do 
        set n = n + r.budget
    end for
- leave:跳出循环
- iterate:跳过剩余语句从循环的开始进入下一个元祖
- if-then-else:
    if 布尔表达式
        then 语句或复合语句
    elseif 布尔表达式
        then 语句或复合语句
    else 语句或复合语句
    end if
- case:
- exception condition & handler:
    declare out_of_classroom_seats condition
    declare exit handler for out_of_classroom_seats
    begin
        sequence of statements
    end

举例:

--确认选课的学生数未超过该课所在教室的容量
--完成学生对该课的注册
--返回错误代码(>=0成功,<0失败)
--以out参数的形式返回失败原因
create function registerStudent(
        in s_id varchar(5),
        in s_courseid varchar(8),
        in s_secid varchar(8),
        in s_semester varchar(6),
        in s_year numeric(4, 0),
        out errorMsg varchar(100))
returns integer
begin

    --选课的学生数
    declare currEnrol int;
    select count(*) into currEnrol
    from takes
    where courseid = s_courseid and
          sec_id =s_secid and
          semester = s_semester and
          year = s_year;

    --该课所在教室的容量
    declare limit int;
    select capacity into limit 
    from classroom natural join section
    where courseid = s_courseid and
          sec_id = s_secid and
          semester = s_semester and
          year = s_year;


    if (currEnrol < limit)
        begin 
            insert into takes values 
        (s_id, s_courseid, s_secid, s_semester, s_year, null);
            return (0);
        end
    --否则,已经达到课程容量上线
    set errorMsg = 'Enrollment limit reached for course' ||
        s_courseid || 'section' || s_secid;
    return (-1);

end;

4. 外部语言过程(略)

触发器

定义:触发器的构成

1.执行条件:触发事件 + 执行条件
2.执行动作

需求:触发器能干什么?

1.用触发器实现复杂的完整性约束
2.满足特定条件自动执行某项任务
3.满足特定条件对用户发出警报

语法:如何编写一个触发器?

--在插入时启动触发器
create trigger timeslot_check after insert on section
    --过渡变量:插入完成后存储所插入行的值
    referencing new row as nrow
    --对每一个插入行迭代
    for each row 
    /* time_slot_id不存在该id*/
    when (nrow.time_slot_id not in 
            (select time_slot_id from time_slot))
    begin
        rollback
    end;
--在删除时启动触发器
create trigger timeslot_check2 after delete on time_slot
    --过渡变量:存储已经更新或者删除行的旧值
    referencing old row as orow
    for each row 
    when( orow.time_slot_id not in 
            (select time_slot_id from time_slot)
            /* 旧id应该在time_slot中*/
        and
        orow.time_slot_id  in 
            (select time_slot_id from section))
            /* 旧id不应该在section*/
    begin
        rollback
    end;
create trigger credits_earned after update of takes on (grade)
    referencing new row as nrow
    referencing old row as orow
    for each row
    /*只有当课程从‘F’或者空值被更新到代表课程成功完成的具体分数时,触发器才被激发*/
    when nrow.grade <> 'F' and nrow.grade is not null 
        and (orow.grade = 'F' or orow.grade is null)
    begin
        update student
        set tot_cred = tot_cred + 
            (select credits
             from course
             where course.id = nrow.courseid)
        where student.id = nrow.id;
    end;
/*若所插入分数的值为空白,则用null替代*/
create trigger setnull before update of takes 
    referencing new row as nrow
    for each row 
    when (nrow.grade = ' ')
    begin atomic
        set nrow.grade = null;
    end;
alter trigger trigger_name disable;//禁用触发器
drop trigger trigger_name;//永久移除触发器
create trigger reorder after update of level on inventory
    referencing old row as orow, new row as nrow
    for each row 
    when nrow.level <= (select level from minlevel where minlevel.item = nrow.item) 
        and
        orow.level > (select level from minlevel where minlevel.item = orow.item))
    begin atomic
        insert into orders
            (select item, amount
             from reorder
             where reorder.item = orow.item)
    end

辨析:触发器不适合干什么?

触发器虽好,但不要滥用!
1)触发器可以替代级联特性来实现外码的on delete cascsde特性
但这样工作量更大,且这种约束难以理解。
2)可以使用触发器来物化视图。
然而,现在很多数据库系统都支持自动视图维护。
3)触发器也被用来复制或备份数据库
现代数据库内置了数据库复制工具
4)

示例(来自课后习题)

--例题5.7
/*
    define/create:
    on/of:
    table:
    statement:
    new table:
*/
define trigger insert_into_branch_cust_via_depositor 
after insert on branch_cust
referencing new table as inserted
for each statement
    insert into branch_cust
        select branch_name, customer_name
        from inserted, account
        where inserted.account_name = account.account_name

define trigger insert_into_branch_cust_via_account
    after insert on branch_cust
    referencing new table as inserted
    for each statement
        insert into branch_cust
            select branch_name, customer_name
            from inserted, depositor
            where depositor.account_name = inserted.account_name

define trigger delete_from_branch_cust_via_depositor
    after delete on depositor
    referencing old table as deleted
    for each statement
        select branch_name, customer_name
        from deleted, account
        where deleted.account_name = account.account_name

define trigger delete from branch cust via account
    after delete on account
    referencing old table as deleted 
    for each statement
        delete from branch cust
        select branch name, customer name
        from depositor, deleted
    where depositor.account number = deleted.account number
define trigger delete_from_account
    after delete on account
    referencing old row as orow 
    for each row 
        delete from depositor
            where depositor.customer_name not in (
                select customer_name
                from depositor
                where account_number <> orow.account_number)

你可能感兴趣的:(数据库)