跳转至

5-2. SQL

一、嵌入式查询(子查询):父查询的“数据来源”

子查询是嵌套在另一个SQL语句(父查询)中的查询,核心作用是为父查询提供过滤条件或数据输入。它的关键区别在于“是否依赖父查询数据”,可分为两类。

1. 子查询的核心分类

类型 定义 执行顺序 典型场景
非相关子查询 子查询不使用父查询中的任何字段,可独立执行 先执行子查询,再将结果传给父查询 IN判断“是否在子查询结果中”
相关子查询 子查询依赖父查询的字段(如你笔记中的s.sno),无法独立执行 类似“for循环”:父查询每读一行,子查询就执行一次 EXISTS判断“父查询行是否存在匹配的子查询结果”

2. 子查询常用运算符:IN / ALL / ANY / EXISTS

这四个运算符是子查询与父查询的“连接桥梁”,用法和场景差异很大,必须区分清楚。

  • IN:判断父查询字段是否“存在于子查询的结果集中”(只要有一个匹配就满足)
    示例:查询选过“c002”课程的学生姓名(非相关子查询写法)

    select Sname
    from student
    where sno IN (select sno from sc where cno = 'c002'); -- 子查询先查所有选c002的学号
    
  • ALL:判断父查询字段是否“满足子查询结果集中的所有条件”(需搭配比较运算符,如>、<)
    示例:查询“所有课程成绩都大于80分”的学生姓名

    select Sname
    from student s
    where 80 < ALL (select grade from sc where sc.sno = s.sno); -- 子查询查该学生所有成绩,需都>80
    
  • ANY:判断父查询字段是否“满足子查询结果集中的任一条件”(同样搭配比较运算符)
    示例:查询“至少有一门课程成绩大于90分”的学生姓名

    select Sname
    from student s
    where 90 < ANY (select grade from sc where sc.sno = s.sno); -- 子查询查该学生成绩,有一个>90即可
    
  • EXISTS:判断“子查询是否能返回结果”(只要子查询有1行结果,就返回true,效率比IN高)
    对应你笔记中的例子(相关子查询经典场景):

    select Sname
    from student s -- 父查询:遍历每一个学生s
    where exists( -- 子查询:判断当前学生s是否有选c002的记录
                 select sc.sno
                 from sc
                 where sc.sno = s.sno -- 子查询依赖父查询的s.sno
                 and sc.cno = 'c002'
                );
    
    ✅ 注意:EXISTS只关心子查询“有没有结果”,不关心结果是什么,所以子查询字段写*也可以(如select * from sc...)。

二、视图(View):虚拟的“结果表”

视图不是真实的表(不存储数据),而是由一个查询语句定义的“虚拟表”。它的核心价值是简化查询、控制数据访问权限,避免直接操作原始表。

1. 视图的核心作用

  • 简化复杂查询:将多表关联、聚合计算的复杂SQL封装成视图,后续查询直接用视图名即可。
  • 权限控制:只给用户开放视图的访问权限(如只能看部分字段),保护原始表的敏感数据(如学生的身份证号)。
  • 数据独立性:即使原始表结构变化,只要视图的查询逻辑调整,依赖视图的程序无需修改。

2. 视图的基本操作:创建、查询、更新、删除

  • 创建视图(对应你笔记的语法,补充完整示例):
    示例:创建“计算机系男生”的视图,只显示学号、姓名、年龄

    create view M_student -- 视图名
    as 
    select sno, Sname, Sage -- 只显示需要的字段,隐藏敏感信息
    from student
    where Sdept = '计算机系' and Ssex = '男'; -- 过滤条件
    
  • 查询视图(和查普通表完全一样):

    select * from M_student where Sage < 20; -- 查询计算机系20岁以下的男生
    
  • 更新视图(有严格限制,不是所有视图都能更新):
    ✅ 可更新的情况:视图由单表查询定义,无DISTINCT、聚合函数(如COUNT)、GROUP BY等。
    示例:修改视图中某学生的年龄

    update M_student set Sage = 21 where sno = '2023001'; -- 本质是修改原始student表的数据
    
    ❌ 不可更新的情况:视图含聚合函数(如create view grade_avg as select sno, avg(grade) from sc group by sno),因为无法反向修改“平均值”对应的原始成绩。
  • 删除视图(只删除视图定义,不影响原始表数据):

    drop view if exists M_student; -- 避免视图不存在时报错
    

3. 视图的权限控制

通过GRANT语句给不同用户分配视图的操作权限,实现“数据隔离”。
示例:给用户“user1”分配“查询M_student视图”的权限,但禁止修改:

GRANT SELECT ON M_student TO user1; -- 只给查询权限
-- 若要收回权限,用 REVOKE SELECT ON M_student FROM user1;

三、SQL约束:保证数据的“正确性和完整性”

约束是对表中数据的规则限制,防止插入无效、错误的数据。你提到了“外键”,这里补充所有常用约束类型,形成完整体系。

1. 常见约束类型

约束名称 关键字 作用 示例(创建student表时添加)
非空约束 NOT NULL 字段值不能为NULL(如学生姓名不能为空) Sname varchar(20) NOT NULL
唯一约束 UNIQUE 字段值在表中唯一(如学号不能重复,可允许NULL) sno varchar(10) UNIQUE
主键约束 PRIMARY KEY 非空+唯一,唯一标识表中每一行(如学号) sno varchar(10) PRIMARY KEY
外键约束 FOREIGN KEY 关联两张表,保证“参照完整性”(如sc表的sno关联student表的sno) 见下文详细示例
检查约束 CHECK 限制字段值的范围(如年龄不能小于0) Sage int CHECK (Sage > 0)

2. 外键约束

外键是“子表”中引用“父表”主键的字段,用来保证两张表的数据逻辑一致(比如“选课记录”的学生,必须是“学生表”中存在的学生)。

  • 语法示例(创建sc表时,用sno关联student表的sno):

    create table sc(
        sno varchar(10), -- 外键字段,对应student表的主键sno
        cno varchar(10),
        grade int,
        -- 定义外键
        FOREIGN KEY (sno) REFERENCES student(sno)
            -- 可选:删除/更新父表数据时的处理规则
            ON DELETE CASCADE -- 若删除student表的某学号,自动删除sc表中该学号的所有选课记录
            ON UPDATE CASCADE -- 若修改student表的某学号,自动修改sc表中该学号的所有记录
    );
    
  • 核心作用

  • 防止插入无效数据:比如往sc表插入一个不存在的学号(如“2023999”),会报错。
  • 防止删除被引用的数据:若不设置ON DELETE CASCADE,直接删除student表中已选课的学号,会报错。