跳转至

博客

PostgreSQL 索引

Indexes are a common way to enhance database performance. An index allows the database server to find and retrieve specific rows much faster than it could do without an index. But indexes also add overhead to the database system as a whole, so they should be used sensibly.

一、引入

比如对于这样一个表和查询:

PostgreSQL SQL Dialect
1
2
3
4
CREATE TABLE test1 (
    id integer,
    content varchar
);
PostgreSQL SQL Dialect
SELECT content FROM test1 WHERE id = xxx;

正常情况下数据库会一行一行地扫描整张表才能得到结果,显然这是个很低效的方法。

于是我们可以通过创建索引,来借助数据结构优化搜索的效率。

PostgreSQL SQL Dialect
CREATE INDEX test1_id_index ON test1 (id);

在表 test1 中基于 id 列建立索引,名为 test1_id_index。

在索引被创建后,系统会随着数据的插入、更新、删除更新索引。

对一张很大的表创建索引可能会花费很长时间,默认情况下 PostgreSQL 允许在创建索引的同时支持读操作,但所有写操作都会阻塞直至索引创建完成,不过这一点可以修改,见 Building Indexes Concurrently

二、索引的类型

三、多列索引

索引也可以基于多列来创建,比如对于下面的表和查询:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE test2 (
  major int,
  minor int,
  name varchar
);
PostgreSQL SQL Dialect
SELECT name FROM test2 WHERE major = constant AND minor = constant;

或许创建一个这样的索引是个不错的选择:

PostgreSQL SQL Dialect
CREATE INDEX test2_mm_idx ON test2 (major, minor);

四、索引与 ORDER BY

索引也可以调整排序顺序,默认为升序。

使用 DESC 标明降序,使用 NULLS FIRSTNULLS LAST 来更改空值的位置。

PostgreSQL SQL Dialect
CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);

专业课程综合实训II 记录-专题一、二

一、创建数据库和数据表

md这呆B文档,字段类型不用标准类型,命名规范不统一,同样是 xno,有的用字符串有的用数字我服了。

有空我改一改改得统一一些。

利用 CREATE 命令 创建6张表(表结构如下):

  1. 学生基本信息表 student
字段名 字段类型 约束条件 字段含义说明
sno varchar(8) Primary key 学号
sname varchar(8) Not null 姓名
ssex varchar(2) '男' 或 '女' 性别
sbirthday date 出生日期
classno varchar(6) Foreign key 班级号
totalcredit smallint 初值为 0 已选修的总学分
  1. 班级信息表 class
字段名 字段类型 约束条件 字段含义说明
classno varchar(6) Primary key 班级号
classname varchar(20) Not null 班级名称
classmajor varchar(20) 所属专业
classdept varchar(20) 所属系别
studentnumber smallint [20..40] 学生数
  1. 课程信息表 course
字段名 字段类型 约束条件 字段含义说明
cno varchar(6) Primary key 课程号
cname varchar(30) Not null 课程名称
ccredit smallint [1..4] 学分
  1. 选修课程信息表 sc
字段名 字段类型 约束条件 字段含义说明
sno varchar(8) Foreign key 学号
cno varchar(6) Foreign key 课程号
grade integer [0..100] 成绩
  1. 教师信息表 teacher
字段名 字段类型 约束条件 字段含义说明
tno integer Primary key 教师号
tname varchar(8) Not null 教师姓名
tsex varchar(2) '男' 或 '女' 性别
tbirthday date 出生日期
ttitle varchar(20) 职称
  1. 教师任课情况表 teaching
字段名 字段类型 约束条件 字段含义说明
tno integer Foreign key 教师号
cno varchar(6) Foreign key 课程号
language varchar(10) 'Chinese' 或 'Bilingual' 或 English 授课语言

可以得到表的引用关系,如下图:

---
title: 表的引用关系
---
classDiagram
    class Class {
        classno
        classname
        classmajor
        classdept
        studentnumber
    }
    class Student {
        sno
        sname
        ssex
        sbirthday
        classno
        Totalcredit
    }
    Class <-- Student

    class Course {
        cno
        cname
        ccredit
    }
    Student <-- Sc
    Course <-- Sc
    class Sc {
        sno
        cno
        grade
    }

    class Teacher {
        tno
        tname
        tsex
        tbirthday
        ttitle
    }
    Teacher <-- Teaching
    Course <-- Teaching
    class Teaching {
        tno
        cno
        language
    }

按照顺序依次创建六张表:

PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE TABLE class (
    classno          varchar(6)   PRIMARY KEY,
    classname        varchar(20)  NOT NULL,
    classmajor       varchar(20),
    classdept        varchar(20),
    studentnumber    smallint     CHECK (studentnumber BETWEEN 20 AND 40)
);
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
8
CREATE TABLE student (
    sno            varchar(8)  PRIMARY KEY,
    sname          varchar(8)  NOT NULL,
    ssex           varchar(2)  CHECK (ssex in ('男', '女')),
    sbirthday      date,
    classno        varchar(6)  REFERENCES class,
    totalcredit    smallint    DEFAULT 0
);
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE TABLE teacher (
    tno          varchar(6)    PRIMARY KEY,
    tname        varchar(8)    NOT NULL,
    tsex         varchar(2)    CHECK (tsex in ('男', '女')),
    tbirthday    date,
    ttitle       varchar(20)
);
PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE course (
    cno        varchar(6)     PRIMARY KEY,
    cname      varchar(30)    NOT NULL,
    ccredit    smallint       CHECK (ccredit BETWEEN 1 AND 4)
);
PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE sc (
    sno      varchar(8)    REFERENCES student,
    cno      varchar(6)    REFERENCES course,
    grade    integer       CHECK (grade BETWEEN 0 AND 100)
);
PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE teaching (
    tno         varchar(6)     REFERENCES teacher,
    cno         varchar(6)     REFERENCES course,
    language    varchar(10)    CHECK (language in ('Chinese', 'Bilingual', 'English'))
);

二、利用 INSERT 命令向六张表中插入数据

  1. class 表内容
classno classname classmajor classdept studentnumber
'Rj0801' '软件 0801' '软件工程' '软件开发' 24
'Rj0802' '软件 0802' '软件工程' '软件开发' 26
'Rj0803' '软件 0803' '软件工程' '数字媒体' 25
'Rj0804' '软件 0804' '软件工程' '软件开发' 25
'Rj0805' '软件 0805' '软件工程' '数字媒体' 24
'Rj0806' '软件 0806' '软件工程' '软件开发' 24
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
INSERT INTO class (classno, classname, classmajor, classdept, studentnumber) VALUES
 ('Rj0801', '软件 0801', '软件工程', '软件开发', 24),
 ('Rj0802', '软件 0802', '软件工程', '软件开发', 26),
 ('Rj0803', '软件 0803', '软件工程', '数字媒体', 25),
 ('Rj0804', '软件 0804', '软件工程', '软件开发', 25),
 ('Rj0805', '软件 0805', '软件工程', '数字媒体', 24),
 ('Rj0806', '软件 0806', '软件工程', '软件开发', 24);
  1. student 表内容
sno sname ssex sbirthday classno
'08300010' '李在' '男' '1991-10-1' 'Rj0801'
'08300012' '葛畅' '男' '1990-8-8' 'Rj0801'
'08300015' '刘晶' '女' '1990-5-22' 'Rj0801'
'08300020' '杨敏' '女' '1989-1-8' 'Rj0801'
'08300030' '胡贤斌' '男' '1990-10-8' 'Rj0801'
'08300048' '赵鸿泽' '男' '1989-6-6' 'Rj0802'
'08300050' '王威' '男' '1990-6-10' 'Rj0802'
'08300067' '赵玮' '女' '1990-8-21' 'Rj0803'
'08300075' '王娜娜' '女' '1991-9-23' 'Rj0803'
'08300088' '秦键' '男' '1989-3-1' 'Rj0803'
'08300100' '田邦仪' '女' '1990-2-26' 'Rj0804'
'08300148' '赵心砚' '男' '1991-4-25' 'Rj0805'
'08300150' '杨青' '女' '1989-11-15' 'Rj0805'
'08300160' '杨玲玲' '女' '1990-12-12' 'Rj0806'
PostgreSQL SQL Dialect
INSERT INTO student (sno, sname, ssex, sbirthday, classno) VALUES
 ('08300010', '李在',   '男', '1991-10-1',  'Rj0801'),
 ('08300012', '葛畅',   '男', '1990-8-8',   'Rj0801'),
 ('08300015', '刘晶',   '女', '1990-5-22',  'Rj0801'),
 ('08300020', '杨敏',   '女', '1989-1-8',   'Rj0801'),
 ('08300030', '胡贤斌', '男', '1990-10-8',  'Rj0801'),
 ('08300048', '赵鸿泽', '男', '1989-6-6',   'Rj0802'),
 ('08300050', '王威',   '男', '1990-6-10',  'Rj0802'),
 ('08300067', '赵玮',   '女', '1990-8-21',  'Rj0803'),
 ('08300075', '王娜娜', '女', '1991-9-23',  'Rj0803'),
 ('08300088', '秦键',   '男', '1989-3-1',   'Rj0803'),
 ('08300100', '田邦仪', '女', '1990-2-26',  'Rj0804'),
 ('08300148', '赵心砚', '男', '1991-4-25',  'Rj0805'),
 ('08300150', '杨青',   '女', '1989-11-15', 'Rj0805'),
 ('08300160', '杨玲玲', '女', '1990-12-12', 'Rj0806');
  1. course 表内容
cno cname ccredit
'800001' '计算机基础' 4
'800002' '程序设计语言' 4
'800003' '数据结构' 4
'810011' '数据库系统' 4
'810013' '计算机网络' 3
'810015' '微机原理与应用' 4
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
INSERT INTO course (cno, cname, ccredit) VALUES
    ('800001', '计算机基础',     4),
    ('800002', '程序设计语言',   4),
    ('800003', '数据结构',       4),
    ('810011', '数据库系统',     4),
    ('810013', '计算机网络',     3),
    ('810015', '微机原理与应用', 4);
  1. sc 表内容
sno cno grade
'08300012' '800003' 88
'08300015' '800003'
'08300020' '800003' 81
'08300030' '800003' 78
'08300048' '800003' 95
'08300100' '810011' 67
'08300148' '810011' 58
'08300150' '810011' 89
'08300160' '810011' 71
PostgreSQL SQL Dialect
INSERT INTO sc (sno, cno, grade) VALUES
   ('08300012', '800003', 88),
   ('08300015', '800003', NULL),
   ('08300020', '800003', 81),
   ('08300030', '800003', 78),
   ('08300048', '800003', 95),
   ('08300100', '810011', 67),
   ('08300148', '810011', 58),
   ('08300150', '810011', 89),
   ('08300160', '810011', 71);
  1. teacher 表内容
tno tname tsex tbirthday ttitle
'000001' '李英' '女' '1975-11-3' '讲师'
'000002' '王大山' '男' '1969-3-2' '副教授'
'000003' '张朋' '男' '1970-2-13' '讲师'
'000004' '陈为军' '男' '1985-8-14' '助教'
'000005' '宋浩然' '男' '1976-4-23' '讲师'
'000006' '许红霞' '女' '1966-2-12' '副教授'
'000007' '徐永军' '男' '1962-1-24' '教授'
'000008' '李桂菁' '女' '1960-12-15 '教授'
'000009' '王一凡' '女' '1974-12-8' '讲师'
'000010' '田峰' '男' '1988-1-18' '助教'
PostgreSQL SQL Dialect
INSERT INTO teacher(tno, tname, tsex, tbirthday, ttitle) VALUES
 ('000001', '李英', '女', '1975-11-3', '讲师'),
 ('000002', '王大山', '男', '1969-3-2', '副教授'),
 ('000003', '张朋', '男', '1970-2-13', '讲师'),
 ('000004', '陈为军', '男', '1985-8-14', '助教'),
 ('000005', '宋浩然', '男', '1976-4-23', '讲师'),
 ('000006', '许红霞', '女', '1966-2-12', '副教授'),
 ('000007', '徐永军', '男', '1962-1-24', '教授'),
 ('000008', '李桂菁', '女', '1960-12-15', '教授'),
 ('000009', '王一凡', '女', '1974-12-8', '讲师'),
 ('000010', '田峰', '男', '1988-1-18', '助教');
  1. teaching 表内容
cno tno language
'800001' '000001' 'English'
'800002' '000002' 'Chinese'
'800003' '000002' 'Bilingual'
'810011' '000003' 'Chinese'
'810013' '000004' 'English'
'800001' '000005' 'Chinese'
'800002' '000006' 'Chinese'
'800003' '000007' 'English'
'810011' '000007' 'English'
'810013' '000008' 'Bilingual'
PostgreSQL SQL Dialect
INSERT INTO teaching (cno, tno, language) VALUES
 ('800001', '000001', 'English'),
 ('800002', '000002', 'Chinese'),
 ('800003', '000002', 'Bilingual'),
 ('810011', '000003', 'Chinese'),
 ('810013', '000004', 'English'),
 ('800001', '000005', 'Chinese'),
 ('800002', '000006', 'Chinese'),
 ('800003', '000007', 'English'),
 ('810011', '000007', 'English'),
 ('810013', '000008', 'Bilingual');

三、简单的数据操作 24 条

  1. 查询所有同学的所有基本信息。
PostgreSQL SQL Dialect
SELECT * FROM student;

查询结果:

Text Only
   sno    | sname  | ssex | sbirthday  | classno | totalcredit
----------+--------+------+------------+---------+-------------
 08300010 | 李在   | 男   | 1991-10-01 | Rj0801  |           0
 08300012 | 葛畅   | 男   | 1990-08-08 | Rj0801  |           0
 08300015 | 刘晶   | 女   | 1990-05-22 | Rj0801  |           0
 08300020 | 杨敏   | 女   | 1989-01-08 | Rj0801  |           0
 08300030 | 胡贤斌 | 男   | 1990-10-08 | Rj0801  |           0
 08300048 | 赵鸿泽 | 男   | 1989-06-06 | Rj0802  |           0
 08300050 | 王威   | 男   | 1990-06-10 | Rj0802  |           0
 08300067 | 赵玮   | 女   | 1990-08-21 | Rj0803  |           0
 08300075 | 王娜娜 | 女   | 1991-09-23 | Rj0803  |           0
 08300088 | 秦键   | 男   | 1989-03-01 | Rj0803  |           0
 08300100 | 田邦仪 | 女   | 1990-02-26 | Rj0804  |           0
 08300148 | 赵心砚 | 男   | 1991-04-25 | Rj0805  |           0
 08300150 | 杨青   | 女   | 1989-11-15 | Rj0805  |           0
 08300160 | 杨玲玲 | 女   | 1990-12-12 | Rj0806  |           0
(14 行记录)
  1. 查询所有男同学的学号、姓名、出生日期
PostgreSQL SQL Dialect
SELECT (sno, sname, sbirthday) FROM student WHERE ssex LIKE '男';

查询结果:

Text Only
             row
------------------------------
 (08300010,李在,1991-10-01)
 (08300012,葛畅,1990-08-08)
 (08300030,胡贤斌,1990-10-08)
 (08300048,赵鸿泽,1989-06-06)
 (08300050,王威,1990-06-10)
 (08300088,秦键,1989-03-01)
 (08300148,赵心砚,1991-04-25)
(7 行记录)
  1. 在基本表 student 中增加 addr:varchar(20)列,然后将其长度由 20 改为 25
PostgreSQL SQL Dialect
ALTER TABLE student ADD COLUMN addr varchar(20);
PostgreSQL SQL Dialect
ALTER TABLE student ALTER COLUMN addr type varchar(25);
  1. 在基本表 student 中增加 register_date:date 列,并为其设置默认值为当前系统时间,再删除该列
PostgreSQL SQL Dialect
ALTER TABLE student ADD COLUMN register_date date DEFAULT CURRENT_TIMESTAMP;
PostgreSQL SQL Dialect
ALTER TABLE student DROP COLUMN register_date;
  1. 在基本表 student 中为 age: integer 列,增加默认值为 18
PostgreSQL SQL Dialect
ALTER TABLE student ADD COLUMN age integer DEFAULT 18;
  1. 在基本表 sc student 中将 sname 设置为唯一值(unique)

你妈的 sc 表哪来的 sname

PostgreSQL SQL Dialect
ALTER TABLE student ADD CONSTRAINT unique_sname UNIQUE (sname);
  1. 基本表 course sc 中创建索引:(sno,cno DESC)

你妈的 course 表哪来的 sno

PostgreSQL SQL Dialect
CREATE INDEX course_index ON sc (sno, cno DESC);
  1. 在基本表 student 中增加约束条件:男生年龄小于 23 岁,女生年龄小于 21 岁。
PostgreSQL SQL Dialect
ALTER TABLE student ADD CONSTRAINT age_constraint CHECK(ssex LIKE '男' AND age < 23 OR ssex LIKE '女' AND age < 21 );
  1. 创建视图 View_80,存放成绩高于 80 分的选课信息, 显示学号、课程号和成绩,使用 with check option 选项。
PostgreSQL SQL Dialect
CREATE VIEW View_80 AS SELECT sno, cno, grade FROM sc WHERE grade > 80 WITH CHECK OPTION;
  1. 在视图 View_80 中查询成绩高于 90 的选课信息。

    PostgreSQL SQL Dialect
    SELECT * FROM View_80 WHERE grade > 90;
    
  2. 在视图 View_80 中依次插入如下元组:

    08301168,810011,87(可插入成功)会违反外键约束,hai shi

    08301167,810011,78(插入不成功)

    PostgreSQL SQL Dialect
    INSERT INTO View_80 VALUES ('08301168', '810011', 87);
    INSERT INTO View_80 VALUES ('08301167', '810011', 78);
    
  3. 在视图 View_80 中依次修改如下元组:

    将(08301168,810011)所对应的成绩改为 90;(可修改成功)

    将(08301168,810011)所对应的成绩改为 70;(修改不成功)

    PostgreSQL SQL Dialect
    UPDATE View_80 SET grade=90 WHERE sno='08301168' AND cno='810011';
    UPDATE View_80 SET grade=70 WHERE sno='08301168' AND cno='810011';
    
  4. 在视图 View_80 中删除如下元组:sno=08301168,cno=810011

    PostgreSQL SQL Dialect
    DELETE FROM View_80 WHERE sno='08301168' AND cno='810011';
    
  5. 查询所有在“1980-01-01”之前出生的女同学的学号、姓名、性别、出生日期

  6. 查询所有姓“李”的男同学的学号、姓名、性别、出生日期

  7. 查询所有用英文授课的教师号、姓名及英语授课的门数

  8. 查询所有职称不是“讲师”的教师号、姓名、职称

  9. 查询虽然选修了课程,但未参加考试的所有同学的学号

  10. 查询所有考试不及格的同学的学号、成绩,并按成绩降序排列

  11. 查询在 1970 年出生的教师号、姓名、出生日期

  12. 查询各个课程号的选课人数

  13. 查询讲授 2 门课以上的教师号

  14. 查询选修了 800001 课程的学生平均分数、最低分数和最高分数

  15. 查询 1960 年以后出生的,职称为讲师的教师的姓名、出生日期,并按出生日期升序排列。

四、复杂数据查询 30条

五、用数据操作语言 DML 完成对表的更新操作 7条

六、存储过程 6条

  1. 创 建 一 个 能 向 学 生 表 student 中 插 入 一 条 记 录 的 存 储 过 程 insert_student,该过程需要 5 个参数,分别用来传递学号、姓名、性 别、出生日期、班级号。 写出调用存储过程 insert_student 的 SQL 语句,向数据表 student 中插 入一个新同学,并提供相应的实参值。
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE OR REPLACE PROCEDURE insert_student(sno text, sname text, ssex text, sbirthday date, classno text) AS
$$
BEGIN
 INSERT INTO student(sno, sname, ssex, sbirthday, classno) VALUES (sno, sname, ssex, sbirthday, classno);
END;
$$
LANGUAGE plpgsql;
PostgreSQL SQL Dialect
CALL insert_student('123123', 'sb', '男', '2023-05-30', 'Rj0801');
  1. 创 建 一 个 向 课 程 表 course 中 插 入 一 门 新 课 程 的 存 储 过 程 insert_course,该存储过程需要三个参数,分别用来传递课程号、课程名、学分,但允许参数“学分”的默认值为 4,即当调用存储过程 insert_course 时,未给第三个参数“学分”提供实参值时,存储过程将按默认值 4 进行运算。调用存储过程 insert_course,向课程表 course 中插入一门新课程。分两种情况(给出第三个参数和未给出第三个参数)写出相应的 SQL 命令,并比较结果。
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE OR REPLACE PROCEDURE insert_course(cno text, cname text, ccredit smallint DEFAULT 4) AS
$$
BEGIN
 INSERT INTO course(cno, cname, ccredit) VALUES (cno, cname, ccredit);
END;
$$
LANGUAGE plpgsql;
PostgreSQL SQL Dialect
CALL insert_course('321', '123', cast(3 as smallint));
  1. 创建一个名称为 query_student 的存储过程,该存储过程的功能是从 数据表 student 中根据学号查询某一同学的姓名、性别、出生日期、班级号。 调用存储过程 query_student,查询学号为“08301050”的姓名、性别、 出生日期、班级号,并写出完成此功能的 SQL 命令。
PostgreSQL SQL Dialect
CREATE OR REPLACE PROCEDURE query_student(qsno text) AS
$$
DECLARE
 _sname text;
 _ssex text;
 _sbirthday date;
 _classno text;
BEGIN
 SELECT sname, ssex, sbirthday, classno INTO _sname, _ssex, _sbirthday, _classno FROM student WHERE sno=qsno;
 RAISE NOTICE 'sname: %, ssex: %, sbirthday: %, classno: %', _sname, _ssex, _sbirthday, _classno;
END;
$$
LANGUAGE plpgsql;
PostgreSQL SQL Dialect
CALL query_student('08300010');
  1. 建立存储过程,输出平均成绩大于 80 分的学生的姓名、性别、年龄和平均成绩。调用该存储过程,并输出相应的结果。
PostgreSQL SQL Dialect
CREATE OR REPLACE PROCEDURE proc4() AS
$$
DECLARE
 curs CURSOR FOR SELECT sname, ssex, age, avg(grade) AS avg_grade FROM (student NATURAL JOIN sc) GROUP BY sname, ssex, age HAVING avg(grade) > 80;
BEGIN
 FOR record IN curs LOOP
     RAISE NOTICE 'sname: %, ssex: %, age: %, avg_grade: %', record.sname, record.ssex, record.age, record.avg_grade;
 END LOOP;
END;
$$
LANGUAGE plpgsql;
  1. 写存储过程显示所有选择了给定学生姓名选择的全部课程的学生的学号、姓名、选课数、平均成绩、总学分
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
8
9
CREATE OR REPLACE PROCEDURE proc5(qsname text) AS
$$
BEGIN
 SELECT sno, sname, count(cno), avg(grade), totalcredit
 FROM student NATURAL JOIN sc GROUP BY sno, sname
 WHERE exists SELECT * FROM;
END;
$$
LANGUAGE plpgssql;

七、触发器 8条

  1. 创建一个当向学生表 student 中插入一新同学时能自动列出全部同学信 息的触发器 display_trigger。 执行存储过程 insert_student,向学生表中插入一个新同学,看触发器 display_trigger 是否被触发。
PostgreSQL SQL Dialect
CREATE OR REPLACE FUNCTION display_student() RETURNS trigger AS
$$
DECLARE
 curs CURSOR FOR SELECT * FROM student;
BEGIN
 FOR record IN curs LOOP
     RAISE NOTICE '%', record;
 END LOOP;
 RETURN NULL;
END;
$$
LANGUAGE plpgsql;
PostgreSQL SQL Dialect
1
2
3
CREATE TRIGGER display_trigger AFTER INSERT
 ON student
 EXECUTE FUNCTION display_student();
PostgreSQL SQL Dialect
CALL insert_student('123', '23232', '女', '1000-01-02', 'Rj0801');
  1. 创建一个触发器,当向学生表 student 中插入一新同学时能自动更新(增 加 1)class 班级表中该生所在班级的总人数。
PostgreSQL SQL Dialect
CREATE OR REPLACE FUNCTION update_class_student_cnt() RETURNS trigger AS
$$
DECLARE
 cnt integer;
BEGIN
 -- RAISE NOTICE '%', NEW;
 SELECT count(*) FROM student INTO cnt WHERE classno = NEW.classno;
 RAISE NOTICE '%', cnt;
 UPDATE class SET studentnumber = cnt WHERE classno = NEW.classno;
 RETURN NULL;
END;
$$
LANGUAGE plpgsql;

不对,nmd 是增加一,不是根据 student 表更新

PostgreSQL SQL Dialect
CREATE OR REPLACE FUNCTION update_class_student_cnt() RETURNS trigger AS
$$
DECLARE
 cnt integer;
BEGIN
 -- RAISE NOTICE '%', NEW;
 -- SELECT count(*) FROM student INTO cnt WHERE classno = NEW.classno;
 -- RAISE NOTICE '%', cnt;
 UPDATE class SET studentnumber = studentnumber + 1 WHERE classno = NEW.classno;
 RETURN NULL;
END;
$$
LANGUAGE plpgsql;
PostgreSQL SQL Dialect
1
2
3
CREATE TRIGGER update_class_student_cnt_trigger AFTER INSERT
 ON student FOR EACH ROW
 EXECUTE FUNCTION update_class_student_cnt();
  1. 写一个触发器阻止将学生成绩降低

RAISE EXCEPTION 'xxx' 即可

来自 New Bing:

PostgreSQL SQL Dialect
-- 创建触发器函数
CREATE OR REPLACE FUNCTION check_update_trigger_func() RETURNS TRIGGER AS $$
BEGIN
  -- 假设 score 表有 id 和 grade 两个字段
  IF NEW.grade < OLD.grade THEN
    RAISE EXCEPTION 'Cannot decrease the grade';
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器
CREATE TRIGGER check_update_trigger BEFORE UPDATE ON score FOR EACH ROW EXECUTE PROCEDURE check_update_trigger_func();

Debian服务器配置Postgresql远程访问

以华为云 HECS 云耀云服务器 Debian 11.1.0 64-bit 为例。

首先进行安装:

Bash
apt-get install postgresql

在安装完成后默认用户名为 postgres,密码为空。

但是目前并不能直接通过 psql -U postgres 来访问数据库,因为 PostgreSQL 在本地连接时的默认认证方式为 peer,会从操作系统内核中获取当前用户名作为允许连接的用户名进行认证。

这可以通过修改 /etc/postgresql/13/main/pg_hba.conf 来改变:

Diff
#
# Database administrative login by Unix domain socket
-local   all             postgres                                peer
+local   all             postgres                                trust

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     peer
host    replication     all             127.0.0.1/32            md5
host    replication     all             ::1/128                 md5

+host    all             all             0.0.0.0/0               trust

顺便在最后添加了一个允许所有IP访问。

然后修改 /etc/postgresql/13/main/postgresql.conf

Diff
-#listen_addresses = 'localhost'          # what IP address(es) to listen on;
+listen_addresses = '*'          # what IP address(es) to listen on;

之后可以使用 systemctl restart postgresql 来重启服务。

可以使用 systemctl enable postgresql 启用开机启动服务。

现在就可以在其他的机器上使用 psql -h xxx.xxx.xxx.xxx -U postgres 来访问。

注:这里的配置只用于学习用途,不保证生产环境下的安全问题。

-Week 1 02-01-cool-overview-final

这里引入了一个 COOL 语言(Classroom Object Oriented Language)是教授自己写的,它被设计为可以在短时间内实现的语言。实际上它的编译器的数量要超过了用它写的程序的数量。

课程的内容将做一个 COOL 到 MIPS 汇编语言的编译器,将包含五个作业: - 写一个 COOL 程序 - 词法分析器 - 解析器 - 语义分析器 - 代码生成

MIPS 是一个用于 20世纪80年代设计的机器的指令集,而现在有一个用于 MIPS 的可以运行在各种设备的模拟器 SPIN。

-PA1

一、环境准备

在虚拟机中的合适位置创建一个目录,用于存放此次作业相关内容。

在目录中执行下面的命令:

  • 如果使用 C++:make -f /usr/class/cs143/assignments/PA2/Makefile
  • 如果使用 Java:make -f /usr/class/cs143/assignments/PA2J/Makefile

然后会多出来一些文件和符号链接。

需要我们完成的只有 cool.flex(使用 C++)或 cool.lex(使用 Java)。

如果要构建词法分析器,则需要运行 make lexer

Git 原理

Book (git-scm.com) 的笔记。

一个 Git仓库 中的 .git/ 目录下一般有四个关键的文件/目录: - HEAD 文件:用于指向当前被检出的分支 - index 文件(还未被创建):用于保存暂存区信息 - objects 目录:存储所有的 Git Objects - refs 目录:存储指向数据的提交对象的指针

一、Git Objects

Git 是一个 content-addressable 文件系统,其核心其实是一个简单的 键值对数据库。 因此,你可以向 Git 仓库插入任何类型的内容,对应的 Git 会借助哈希返回一个唯一的键值,通过键值可以在任意时刻再次取回内容。

前面提到 Git 的核心其实是一个 键值对数据库,而键通过哈希得到。

如果你随便找一个仓库,逛一逛它的 .git/objects 目录,可以发现里面有很多两个字符的命名的文件夹在其内有一个或多个38个字符命名的文件。将它们连接在一起可以得到一个40个字符的完整的 SHA-1值,这便是数据的 键值,而数据本身就被存储在文件中。

为了存储不同的信息,Git 对象主要有三种:blobtreecommit

  • tree 对象:存储目录结构
  • commit 对象:存储提交信息

下面将依次进行讲解。

1. blob 对象

blob 对象用于存储文件内容数据。

Git 的一个底层命令 git hash-object 可以计算并返回传入的数据,也可以将其写入 .gits/objects 目录(Git Objects 数据库),下面我们将使用这个命令来进行一些尝试。


首先初始化一个新的仓库:

Bash Session
1
2
3
4
$ mkdir GitPlayground
$ cd GitPlayground
$ git init
Initialized empty Git repository in /mnt/d/_Dev/GitPlayground/.git/

在仓库刚被创建的时候 .git/objects 目录会被初始化,其中有两个子目录 infopack,不过目前 .git/objcets 目录中没有任何一个文件:

Bash Session
1
2
3
4
5
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f

使用 git hash-object 创建一个新的数据对象并使用 -w 指示 Git 将其存储到数据库中:

Bash Session
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4

它返回了一个 40 个字符长度的字符串,这是数据 test content 的 SHA-1 哈希值。

现在再查看一下 .git/objects 中的内容:

Bash Session
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

Git 将上面的 SHA-1 哈希值的前 2 个字符作为子目录名,后面 38个字符作为文件名将数据存储为文件。


下面介绍另一个命令 git cat-file,这个命令可以用来很方便地查看 Git Objects 的内容:

Bash Session
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content

下面创建一个新文件并将其写入数据库:

Bash Session
1
2
3
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30

然后修改其内容,再写入数据库:

Bash Session
1
2
3
$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

现在 .git/objects 中就会包含三个文件,分别存储了先前的字符串以及 test.txt 的两个版本:

Bash Session
1
2
3
4
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

现在就算我们将 test.txt 删除,也可以通过唯一的键值获取到对应版本的内容:

Bash Session
1
2
3
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
version 1
Bash Session
1
2
3
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
$ cat test.txt
version 2

这就是 blob 对象:

Bash Session
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob

不过你其实可以注意到,blob 对象只能够存储文件的数据内容,而不能存储目录结构以及文件名等信息。

2. tree 对象

Tree 对象用于存储存储目录结构(文件路径、文件名等)。 快照其实就是存储根目录信息的 tree 对象。


这里先以一个假设的仓库为例解释一下 tree 对象的概念:

假设有一个仓库,其最新的 tree 如下:

Bash Session
1
2
3
4
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README
100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib

master^{tree} 指定了 master 分支最新的提交所指向的 tree 对象。

可以看到 tree 对象的内容包含一系列 Git 对象的关联模式、类型、哈希值以及文件名。 这与 Unix 的文件系统很相似,不过是经过简化的。

如果进一步查看 lib 对象的内容可以得到:

Bash Session
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b      simplegit.rb

其结构可以用下面这张图来表示:

image-20230324205004043


接下来进行一些尝试:

Git 创建 tree 时会使用 暂存区 或 索引 的状态来创建,所以我们要想创建一个 tree 对象,也需要通过暂存一些文件来创建索引。

以一个单入口 test.txt 文件为例:

Bash Session
$ git update-index --add --cacheinfo \
  100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt

通过 git update-index 命令来更新索引,使用 --add 是因为 test.txt 目前并不在暂存区内(甚至暂存区都还未创建),使用 --cacheinfo 是因为 test.txt 目前不在目录中而是在数据库中。 之后指定模式、哈希值、文件名。

100644 表示是一个普通文件,其他更多的模式比如 100755 表示可执行文件,120000 表示一个符号链接。

现在索引创建完毕,可以使用 git write-tree 来将暂存区写入到 tree 对象中并保存进数据库。

Bash Session
1
2
3
4
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt

接下来再创建一个由第二个版本的 test.txt 以及一个新文件 new.txt 组成的 tree 对象:

Bash Session
1
2
3
4
$ echo 'new file' > new.txt
$ git update-index --cacheinfo 100644 \
  1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index --add new.txt
Bash Session
1
2
3
4
5
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt

接下来可以通过 git read-tree 来读取 tree 对象的内容并放到暂存区内,我们取出第一个 tree 的内容置于 bak 目录(使用 --prefix 可以指定存储 tree 对象的目录)然后再创建一个 tree 对象:

Bash Session
1
2
3
4
5
6
7
$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579      bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt

现在整个仓库的状态可以用下图表示:

image-20230324205016509

这便是 tree 对象。

3. commit 对象

到目前为止,blobtree 对象虽然可以存储所有文件及目录的信息,但是仍旧没有保存下来有关谁在何时为何保存了快照的信息,而这些信息就由 commit 对象保存。

可以通过 git commit-tree 并指定一个 tree 对象来创建 commit 对象:

Bash Session
$ echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Bash Session
1
2
3
4
5
6
$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700

First commit

一个 commit 对象包含以下内容: - 用于表示当前快照的顶级的 tree 对象 - 前一个 commit 对象(如果有) - 作者和提交者的相关信息(用户名称以及邮箱还有时间戳) - 提交信息

下面再创建两个 commit 对象,并使用 -p 来指定前一个提交:

Bash Session
1
2
3
4
$ echo 'Second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'Third commit'  | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9

其实目前,我们几乎通过手动操作得到了一个实际的 Git 仓库,可以使用 git log 来查看历史记录:

Bash Session
$ git log --stat 1a410e
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:15:24 2009 -0700

    Third commit

 bak/test.txt | 1 +
 1 file changed, 1 insertion(+)

commit cac0cab538b970a37ea1e769cbbde608743bc96d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:14:29 2009 -0700

    Second commit

 new.txt  | 1 +
 test.txt | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:09:34 2009 -0700

    First commit

 test.txt | 1 +
 1 file changed, 1 insertion(+)

现在再查看一下 .git/objects(注释表示存储的内容):

Bash Session
$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1

整个仓库的内容可以表示为下图:

image-20230324205031736

二、Git References

到目前为止,我们从 Git仓库 取东西都需要一个对应对象的哈希值,Git引用 就是一个特殊的文件,通过保存不同的哈希值来动态地指向不同的 Git对象,他们被存储在 .git/refs 目录下。

对于我们刚才手动创建的“仓库”,目前并没有任何引用:

Bash Session
1
2
3
4
5
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f

若要创建一个新引用来帮助记忆最新提交所在的位置,从技术上讲我们只需简单地做如下操作:

Bash Session
$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master

现在,你就可以在 Git 命令中使用这个刚创建的新引用来代替 SHA-1 值了:

Bash Session
1
2
3
4
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

不过并不建议直接手动修改文件, 如果想更新某个引用,Git 提供了一个更加安全的命令 update-ref 来完成此事:

Bash Session
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

这基本就是 Git 分支的本质:一个指向某一系列提交之首的指针或引用。 若想在第二个提交上创建一个分支,可以这么做:

Bash Session
$ git update-ref refs/heads/test cac0ca

这个分支将只包含从第二个提交开始往前追溯的记录:

Bash Session
1
2
3
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

现在,仓库看起来会像是这样:

image-20230324205042844

1. HEAD 引用

HEAD 文件通常是一个符号引用(symbolic reference),指向目前所在的分支。 所谓符号引用,表示它是一个指向其他引用的指针。

然而在某些罕见的情况下,HEAD 文件可能会包含一个 git 对象的 SHA-1 值。 当你在检出一个标签、提交或远程分支,让你的仓库变成 “分离 HEAD”状态时,就会出现这种情况。

如果查看 HEAD 文件的内容,通常我们看到类似这样的内容:

Bash Session
$ cat .git/HEAD
ref: refs/heads/master

如果执行 git checkout test,Git 会像这样更新 HEAD 文件:

Bash Session
$ cat .git/HEAD
ref: refs/heads/test

当我们执行 git commit 时,该命令会创建一个提交对象,并用 HEAD 文件中那个引用所指向的 SHA-1 值设置其父提交字段。

你也可以手动编辑该文件,然而同样存在一个更安全的命令来完成此事:git symbolic-ref。 可以借助此命令来查看 HEAD 引用对应的值:

Bash Session
$ git symbolic-ref HEAD
refs/heads/master

同样可以设置 HEAD 引用的值:

Bash Session
1
2
3
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test

不能把符号引用设置为一个不符合引用规范的值:

Bash Session
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/

2. Tags 引用

前面我们刚讨论过 Git 的三种主要的对象类型(数据对象树对象提交对象 ),然而实际上还有第四种。 标签对象(tag object) 非常类似于一个提交对象——它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。 主要的区别在于,标签对象通常指向一个提交对象,而不是一个树对象。 它像是一个永不移动的分支引用——永远指向同一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了。

-04-02-finite-automata

有限自动机

一个有限自动机所对应的语言就是接受字符串的集合

正则表达式是声明,而有限自动机则是实现。

一个有限自动机由以下部分组成:

  • 一个输入字母表 \(\Sigma\)
  • 一组状态的有限的集合 \(S\)
  • 一个起始状态 \(n\)
  • 一组可接受的状态 \(F \subseteq S\)
  • 一组状态的转移 \(state \to^{input} state\)

Transition $$ S_1 \to^{a} S_2 $$ 读作:在状态 \(S_1\) 时输入 \(a\) 到状态 \(S_2\)

如果在输入结束的时候状态位于 \(F\) 内则为接受,否则为拒绝(在状态 \(S \notin F\) 终止 或 卡住)

状态图

每一个节点是一个状态,由一个五源的箭头指向的一个节点为起始节点,双边框节点为一个接收状态,箭头为一个转移

stateDiagram-v2
    [*] --> Still
    Still --> [*]

    Still --> Moving
    Moving --> Still
    Moving --> Crash
    Crash --> [*]

image-20230323110644981

不消耗输入的状态移动,可做选择移动与否

image-20230323110756233

image-20230323111011406

image-20230323111213522