1. SQL 概述
SQL 的五个特点
⑴ 综合统一(DDL, DML, DCL);⑵ 高度非过程化;⑶ 面向集合的操作方式;⑷ 以同一种语法结构提供两种使用方式——既是自含式语言,又是嵌入式语言;⑸ 语言简捷,易学易用。
使用的动词
| SQL 功能 | 动词 |
|---|---|
| 数据定义 DDL | CREATE、DROP、ALTER |
| 数据查询 DQL | SELECT |
| 数据更新 DML | INSERT、UPDATE、DELETE |
| 数据控制 DCL | GRANT、REVOKE |
- 动词的类别决定了它改的是"结构"还是"数据"——DDL 三个动词动的是对象结构(库、表、视图、索引),DML 三个动词动的是表里的行。第一部分全是 DDL。
- 要点:课件原文"通常 SQL 语言中不区分大小写,部分数据库提供参数可配置"——指的是关键字;库名/表名在 Linux 上的 MySQL 默认是区分大小写的。
2. 数据定义总览与库对象层次
| 操作对象 | 创建 | 删除 | 修改 |
|---|---|---|---|
| 库 | CREATE DATABASE | DROP DATABASE | ALTER DATABASE |
| 表 | CREATE TABLE | DROP TABLE | ALTER TABLE |
| 视图 | CREATE VIEW | DROP VIEW | ALTER VIEW |
| 索引 | CREATE INDEX | DROP INDEX | —(索引没有修改,见 [[2 索引]]) |
| 名称 | MySQL(课件 16 页) | PostgreSQL(课件 19 页) |
|---|---|---|
| 数据库集群 cluster | 可包含 1 个或多个数据库 | 可包含 1 个或多个数据库 |
| 数据库 database | 可包含 1 个或多个基本表 | 可包含 1 个或多个模式 |
| 模式 schema | 等于 database(MySQL 中两者等价) | 包含表/视图/索引,默认public;另有系统模式pg_catalog(系统表、内置数据类型与函数) |
| 表 table | 存放数据的基本表 | 存放数据的基本表 |
| 表空间 tablespace | InnoDB:共享表空间(所有数据放一个表空间里,可跨多个文件)/ 独立表空间(默认,每个表独立,索引与数据分离) | 逻辑上分出的存储单元,可按快慢盘分别放置(频繁用的索引放快盘、归档表放慢盘) |
- MySQL 的库就是模式,PG 的库里还能再分模式。所以
USE 库名只是 MySQL 的写法,PG 里没有。 - 基本表(base table):本身独立存在的表,
CREATE TABLE建出来的就是它——一个关系对应一个基本表,数据实际存在它里面;与之相对的是视图(虚表,只存定义不存数据)。 - 模式(schema):本课这个词有两层意思——① 教材三级模式里的模式(概念模式),指"数据库中全体数据的逻辑结构和特征",是所有用户的公共数据视图;② DBMS 实现里的schema,指一组数据库对象(表、视图、索引)的命名容器,上表说的"MySQL 里 schema = database"是第二层意思。
- DATABASE 和 TABLE 的区别:库是容器,表是容器里一张二维表。一个库能装多张表,一张表只能属于一个库(所以要先
USE 库名再建表);CREATE DATABASE建容器,CREATE TABLE建容器里的表。
3. 建库:CREATE / 查看 / ALTER / DROP DATABASE(课件 22~23 页)
-- ① 建库(课件 22 页)CREATEDATABASEstudent;-- 课件原例:最简形态CREATEDATABASEstudent-- 把字符集和排序规则一次交代清楚DEFAULTCHARACTERSETutf8mb4-- 字符集 utf8mb4DEFAULTCOLLATEutf8mb4_0900_ai_ci;-- 排序规则 utf8mb4_0900_ai_ci(_ci = 不区分大小写)-- ② 查看(课件 23 页"查看数据库的信息")SHOWDATABASES;-- 列出服务器上所有库SHOWCREATEDATABASEstudent;-- 看这个库的建库语句(字符集、排序规则在这里)USEstudent;-- 切到该库:此后不带库名的表都建在它下面SELECTDATABASE();-- 确认当前在哪个库-- ③ 改库(课件 23 页原例)ALTERDATABASEmydbREADONLY=0-- 0 可读写 / 1 只读DEFAULTCOLLATEutf8mb4_bin;-- _bin = 按字节比较,区分大小写-- ④ 删库(课件 23 页原例)DROPDATABASEIFEXISTSstudent;-- IF EXISTS:库不存在时只警告不报错- 基础操作,比较简单
- 要点:改库只能改库级选项(只读开关、排序规则等),改不了库名;
DROP DATABASE连库里的表一起删,是不可回滚的 DDL。
4. 建表:CREATE TABLE
4.1 语法与两类约束
CREATETABLE<表名>(<列名><数据类型>[列级完整性约束],[<列名><数据类型>[列级完整性约束]]…,[<表级完整性约束>]);- 列级完整性约束条件:只涉及一个属性列的约束。
- 表级完整性约束条件:涉及一个或多个属性列的约束(组合主码、组合外码只能写这里)。
4.2 数据类型
-- 整数类型 字节数 说明 范围smallint-- 2 小范围整数 -32768 ~ 32767int-- 4 常用的整数 -2^31 ~ 2^31-1bigint-- 8 大范围整数 -2^63 ~ 2^63-1-- 浮点与定点类型 字节数 说明decimal(m,n)-- 可变长 用户指定的精度,精确;总共 1~65 位,小数点后 0~30 位numeric(m,n)-- 可变长 MySQL 中等于 decimalfloat-- 4 可变精度,不精确;约 ±10^38,7 位有效数字-- 例:decimal(5,2) 的范围是 -999.99 ~ 999.99-- 字符类型 作用char(n)-- 定长字符数据,不足补空白varchar(n)-- 变长字符数据,有长度限制text-- 变长字符数据,无长度限制-- 日期类型date-- 日期 '2021-01-01',范围 '1000-01-01' ~ '9999-12-31'datetime-- 日期时间 '2021-01-01 10:10:29',范围 '1000-01-01 00:00:00.000000' ~ '9999-12-31 23:59:59.999999'4.3 完整性约束
-- 课件列出的约束:Primary key, Foreign key, Unique, Null, default, CHECK, auto_incrementCREATETABLEperson(idINTNOTNULLAUTO_INCREMENTPRIMARYKEY,-- 主码 + 自动增长nameVARCHAR(8),-- [Null](不写 NOT NULL 就是允许空)INDEXix_person_name(name)-- 建表时顺带建索引);这些完整性约束条件被存入系统的数据字典中。
| 约束 | 含义 | 列级 | 表级 |
|---|---|---|---|
PRIMARY KEY | 主码:非空且唯一,每表一个 | ✅ | ✅(组合主码只能写这里) |
FOREIGN KEY … REFERENCES | 外码:取值必须是被参照表里已有的值 | ✅ | ✅ |
NOT NULL | 不允许为空 | ✅ | — |
UNIQUE | 取值唯一(主码之外还想唯一的列) | ✅ | ✅ |
DEFAULT 值 | 不给值时用什么 | ✅ | — |
CHECK (条件) | 取值要满足条件 | ✅ | ✅ |
AUTO_INCREMENT | 整数列自动加一(MySQL) | ✅ | — |
- 总体:约束是写在建表语句里的规则,由 DBMS 存进数据字典并在每次增删改时自动检查。
- 大思路:这些约束正对应教材里的三类完整性——主码=实体完整性,外码=参照完整性,其余(非空/唯一/CHECK/默认)=用户定义完整性。
- 要点:
PRIMARY KEY本身已经含NOT NULL,列上再写一遍NOT NULL(课件例一就是这么写的)不报错但多余。
4.4 建表例题
-- [例1](课件 28 页)建立"学生"表 StudentCREATETABLEStudent(SnoCHAR(8)NOTNULL,-- 学号:不能为空SnameCHAR(20)UNIQUE,-- 姓名:取值唯一(MySQL 会自动建一个名为 Sname 的唯一索引)SgenderCHAR(6),-- 性别SbirthdateDATE,-- 出生日期SmajorCHAR(40),-- 所在系PRIMARYKEY(Sno)-- 主码写成了表级约束);-- [例2](课件 29 页)建立"学生选课"表 SCCREATETABLESC(SnoCHAR(8),-- 学号CnoCHAR(3),-- 课程号GradeINT,-- 成绩PRIMARYKEY(Sno,Cno),-- 组合主码:一个学生一门课只有一条记录FOREIGNKEY(Sno)REFERENCESS(Sno),-- 外码①:学号必须在 S 表中存在FOREIGNKEY(Cno)REFERENCESC(Cno)-- 外码②:课程号必须在 C 表中存在);- 语法上要求什么(能不能建成):被参照的表
S必须已经存在,S里必须有Sno这一列,而且这一列要是S的主码或候选码(MySQL 里就是这一列上必须有主键或唯一索引,随便一个普通列不能被引用),两边类型也要相容;不满足的话建表时直接报错。 - 数据上要求什么(插数据时的检查):
SC里每一行的Sno取值,必须在S表里找得到对应的一行——插入或修改SC时 DBMS 会去S表核对,找不到就报错。反方向不要求:S里的学生可以一行选课记录都没有。 - 所以不是"一一对应",是"多对一":
SC.Sno可以重复(一个学生选多门课),S里同一个Sno也可以被引用多次、或者一次都不被引用。它保证的只有一件事——不会出现"选了不存在的学生的课"。 - 外码列没写
NOT NULL时允许为空,为空的行不检查(NULL不指向任何一行)。
5. 改表与删表
5.1 改表:ALTER TABLE
-- 课件(教材)给的语法格式ALTERTABLE<表名>[ADD<新列名><数据类型>[完整性约束]]-- 增加新列和新的完整性约束条件[ADD<完整性约束名><列名>][DROP<完整性约束名><列名>]-- 删除指定列或列的完整性约束条件[ALTERCOLUMN<列名><数据类型>];-- 修改列名和数据类型-- MySQL 8.0 上实际能执行的动作(一一对应)ALTERTABLEStudentADDCOLUMNSemailVARCHAR(30);-- 加列ALTERTABLEStudentMODIFYCOLUMNSbirthdateVARCHAR(20);-- 改列的数据类型ALTERTABLEStudent CHANGECOLUMNSbirthdate SbirthVARCHAR(20);-- 改列名 + 改数据类型ALTERTABLEStudentDROPCOLUMNSemail;-- 删列总体:四类动作——加列、改列、删列、改约束。
注意:课件的
ALTER COLUMN是教材/标准写法,MySQL 里要写成MODIFY COLUMN(改类型)或CHANGE COLUMN(改列名+类型),理论课按课件写,上机按 MySQL 写。"整列重定义"的意思:
MODIFY COLUMN 列 新定义是把这一列的定义整条换成你写的内容,不是只替换你写了的那几项;没写出来的属性一律回到默认值(可空、无默认值),而且不报错:CREATETABLEt(aVARCHAR(5)NOTNULLDEFAULT'x',bINT);ALTERTABLEtMODIFYCOLUMNaVARCHAR(10);-- 只想把长度改成 10DESCt;-- 结果 a 变成 Null=YES、Default=NULL:NOT NULL 和 DEFAULT 都丢了-- 要改就把完整定义重写一遍ALTERTABLEtMODIFYCOLUMNaVARCHAR(10)NOTNULLDEFAULT'x';类型转换不合法的情况确实有:执行
ALTER时 DBMS 要把每一行的旧值按新类型重转一遍,转不过去的典型是——变长改短(VARCHAR(20)→VARCHAR(5),严格模式下报ERROR 1406 Data too long)、字符转数字('abc'转int,报ERROR 1265或被截成 0)、日期字符串格式不对,以及改被外码引用的列(外码两边类型必须一致,直接报错)。碰到了怎么办:① 先
SELECT找出会被破坏的行(如WHERE CHAR_LENGTH(a) > 5);② 备份或先清理(CREATE TABLE t_bak AS SELECT * FROM t;);③ 再执行ALTER;④ 改完用DESC+SELECT复查。——课件例4 那句注"修改原有的列定义有可能会破坏已有数据"说的就是这件事。
5.2 例三
-- [例3] 向学生表增加"邮箱地址"列,数据类型为字符型ALTERTABLEStudentADDSemailVARCHAR(30);-- 课件注:不论基本表中原来是否已有数据,新增加的列一律为空值-- [例4] 将 student 表中出生日期的数据类型改为字符型ALTERTABLEStudentALTERCOLUMNSbirthdateVARCHAR(20);-- 课件注:修改原有的列定义有可能会破坏已有数据-- [例5] 增加学生名称必须取唯一值的约束条件ALTERTABLEStudentADDUNIQUE(Sname);-- [例6] 删除学生姓名必须取唯一值的约束ALTERTABLEStudentDROPCONSTRAINTIX_sname;5.3 删表:DROP TABLE(课件 32 页)
DROPTABLEStudent;-- [例7] 删除 Student 表- 要点:表被别的表的外码引用时,直接删会报错,得先处理子表或外码;上机脚本里为可重复执行通常写
DROP TABLE IF EXISTS Student;。 - "被外码引用就删不掉"是什么意思:如果别的表(子表,如
SC)上有外码指向Student,DROP TABLE Student;会被 DBMS 拒绝(MySQL 报ERROR 3730 Cannot drop table 'student' referenced by a foreign key constraint)——删了之后子表的外码就指向不存在的数据,参照完整性被破坏。 - 想删就得先处理子表:先删子表
DROP TABLE SC;,或先删子表上的外码ALTER TABLE SC DROP FOREIGN KEY 外码名;,再删父表。注意建表时写的ON DELETE CASCADE只管"删行"、管不了删表;标准 SQL 的DROP TABLE Student CASCADE;在 MySQL 里能写但不生效。除了被外码引用,删不掉的常见原因就只剩权限不够(当前用户没有这张表的DROP权限)。 IF EXISTS的作用:表不存在时直接DROP会报错(MySQL 报ERROR 1051 Unknown table),加了IF EXISTS就只给一条警告、脚本继续往下跑——让建表脚本能反复执行。另外DROP TABLE属于 DDL,删了不能回滚。