建表时声明约束是数据库设计的基础,常见的有主键约束、外键约束和 CHECK 约束。
场景:供应商-零件多对多关系
经典的供应商(Supplier)-零件(Part)-供应(Supply)三表结构:
- 供应商可以供应多种零件(M:N 关系)
- 每对供应商+零件组合有一个供应价格
关系模式(3NF)
Supplier(Sno PK, Sname, City)
Part(Pno PK, Pname, Color, Weight)
Supply(Sno PK FK, Pno PK FK, Price)
Supply 的主键是复合主键 (Sno, Pno),同时 Sno 和 Pno 各自是外键。
SQL 建表
CREATE TABLE Supplier (
Sno CHAR(10) NOT NULL,
Sname VARCHAR(50) NOT NULL,
City VARCHAR(50),
PRIMARY KEY (Sno)
);
CREATE TABLE Part (
Pno CHAR(10) NOT NULL,
Pname VARCHAR(50) NOT NULL,
Color VARCHAR(20),
Weight DECIMAL(8,2),
PRIMARY KEY (Pno)
);
CREATE TABLE Supply (
Sno CHAR(10) NOT NULL,
Pno CHAR(10) NOT NULL,
Price DECIMAL(10,2) NOT NULL,
PRIMARY KEY (Sno, Pno),
FOREIGN KEY (Sno) REFERENCES Supplier(Sno)
ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (Pno) REFERENCES Part(Pno)
ON DELETE CASCADE ON UPDATE CASCADE,
CHECK (Price > 0)
);
各约束说明
PRIMARY KEY:不能为空,不能重复。复合主键用 PRIMARY KEY (col1, col2) 表格级声明,不能用列级。
FOREIGN KEY:引用另一张表的主键或唯一键,保证参照完整性。ON DELETE CASCADE 表示父表删除时子表自动级联删除。
CHECK:定义列值的范围或条件,CHECK (Price > 0) 要求价格必须大于零。
NOT NULL:禁止 NULL 值,通常和主键一起用。
常用查询
查询”供应红色零件的供应商名称”:
SELECT DISTINCT s.Sname
FROM Supplier s
JOIN Supply sp ON s.Sno = sp.Sno
JOIN Part p ON sp.Pno = p.Pno
WHERE p.Color = '红色';
或用子查询:
SELECT Sname
FROM Supplier
WHERE Sno IN (
SELECT sp.Sno
FROM Supply sp
JOIN Part p ON sp.Pno = p.Pno
WHERE p.Color = '红色'
);
约束的列级 vs 表级写法
列级(约束跟在列后面,只作用于该列):
CREATE TABLE t (
id INT PRIMARY KEY,
val INT CHECK (val > 0),
fk INT REFERENCES other(id)
);
表级(单独一行,支持多列主键和复合外键):
CREATE TABLE t (
id1 INT,
id2 INT,
val INT,
PRIMARY KEY (id1, id2), -- 复合主键必须用表级
FOREIGN KEY (id1) REFERENCES a(id),
CHECK (val > 0)
);
复合主键(多列联合唯一)只能用表级声明;单列约束两种写法均可。
