SQL CREATE TABLE:定义表
第 9.1 课 · 阅读时间:约 9 分钟 · 练习题 ↓
SQL CREATE TABLE 语句创建一张新表:它的列、各列的数据类型,以及数据必须遵守的规则。在本课中,你将学习如何定义列、选择数据类型,以及使用主要的约束:PRIMARY KEY、NOT NULL、UNIQUE、DEFAULT、CHECK 和 FOREIGN KEY。
到目前为止,你使用的都是已经存在的表。CREATE TABLE 属于数据定义语言(DDL):这类命令定义的是数据库的结构,而不是其中的数据。设计良好的表能紧凑地存储数据、拒绝无效值,并保持表与表之间关系的一致性。
示例使用 MySQL 和 MariaDB 语法。SQLtest 上的示例数据库是只读的,因此请在练习场中选择不含示例数据的数据库来练习这些语句。
CREATE TABLE 语法
CREATE TABLE table_name (
column1 data_type constraints,
column2 data_type constraints,
table_constraints
);
每一列都有名称、数据类型,还可以有约束和其他参数。涉及多列的约束(例如复合主键)写在所有列之后。
第一张表
CREATE TABLE students (
student_id INT,
first_name VARCHAR(50),
last_name VARCHAR(50),
birth_date DATE,
created_at TIMESTAMP
);
结果:一张有五列的空表 students。
这张表可以使用,但它什么都接受:重复的 id、没有名字的学生。约束可以解决这些问题。
选择数据类型
| 数据类型 | 存储内容 | 示例 |
|---|---|---|
INT、BIGINT | 整数 | id、数量 |
DECIMAL(p, s) | 带 s 位小数的精确数值 | 金额:DECIMAL(10, 2) |
VARCHAR(n) | 最多 n 个字符的文本 | 姓名、电子邮件 |
TEXT | 长文本 | 描述 |
DATE | 日历日期 | 出生日期 |
DATETIME、TIMESTAMP | 日期和时间 | 创建时间 |
BOOLEAN | 真或假(MySQL 中为 TINYINT(1)) | 标志 |
选择能容纳数据的最窄类型:这样既节省空间,又能拒绝错误的值。金额请存为 DECIMAL,不要用 FLOAT,因为它无法精确表示许多十进制小数。详见数据类型一课。
主键和 AUTO_INCREMENT
主键标识每一行:它的值唯一且永远不为 NULL。代理键通常由数据库通过 AUTO_INCREMENT 自动生成:
CREATE TABLE students (
student_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL
);
INSERT INTO students (first_name, last_name)
VALUES ('ANNA', 'LEE'), ('MARK', 'STONE');
SELECT * FROM students;
结果:两名学生,student_id 分别为自动生成的 1 和 2。
其他数据库生成键的方式不同:PostgreSQL 使用 GENERATED ALWAYS AS IDENTITY 或 SERIAL,SQL Server 使用 IDENTITY(1, 1)。
NOT NULL、UNIQUE 和 DEFAULT
CREATE TABLE employees (
employee_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
hire_date DATE NOT NULL,
salary DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
NOT NULL:该列必须有值。UNIQUE:任意两行不能有相同的值。允许NULL值,且多个NULL不算重复。DEFAULT:当INSERT没有提供值时使用的值。
-- 错误:Column 'hire_date' cannot be null
INSERT INTO employees (first_name, last_name, hire_date)
VALUES ('JOHN', 'DOE', NULL);
CHECK 约束
CHECK 定义每一行都必须满足的条件:
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
discount_pct INT DEFAULT 0 CHECK (discount_pct BETWEEN 0 AND 90)
);
-- 错误:Check constraint 'products_chk_1' is violated
INSERT INTO products (product_name, price) VALUES ('Broken', -5);
MySQL 从 8.0.16 版本开始强制执行 CHECK,MariaDB 从 10.2 开始;更早的 MySQL 版本接受这种语法,但会忽略其中的条件。
外键
外键把一列与另一张表的主键关联起来。之后,数据库会拒绝被引用表中不存在的值:
CREATE TABLE departments (
department_id INT AUTO_INCREMENT PRIMARY KEY,
department_name VARCHAR(100) NOT NULL UNIQUE
);
CREATE TABLE staff (
staff_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(100) NOT NULL,
department_id INT NOT NULL,
FOREIGN KEY (department_id) REFERENCES departments (department_id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
INSERT INTO departments (department_name) VALUES ('Sales');
INSERT INTO staff (full_name, department_id) VALUES ('ANNA LEE', 1);
-- 错误:a foreign key constraint fails(部门 99 不存在)
INSERT INTO staff (full_name, department_id) VALUES ('MARK STONE', 99);
ON DELETE 规定当被引用的行被删除时,子行会发生什么:
| 选项 | 删除父行时 |
|---|---|
RESTRICT / NO ACTION | 只要还有子行,删除就会被拒绝(默认) |
CASCADE | 子行也一起被删除 |
SET NULL | 子表中的该列被设为 NULL |
当被引用的键发生变化时,ON UPDATE 的工作方式相同。Sakila 中的大多数外键使用 ON DELETE RESTRICT,这就是为什么在第 8.3 课中无法删除有租赁记录的客户。
IF NOT EXISTS
创建已经存在的表会报错。IF NOT EXISTS 会在这种情况下跳过创建,这在需要多次运行的脚本中很有用:
CREATE TABLE IF NOT EXISTS departments (
department_id INT AUTO_INCREMENT PRIMARY KEY,
department_name VARCHAR(100) NOT NULL
);
注意它不会比较表结构:如果同名的表已经存在但列不同,它会保持原样。
文本编码
对于文本列,MySQL 和 MariaDB 允许设置字符集和排序规则(collation):
CREATE TABLE customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) DEFAULT CHARSET = utf8mb4;
utf8mb4 可以存储任何 Unicode 字符,包括表情符号;排序规则决定字符串如何比较和排序。从 MySQL 8.0 开始,utf8mb4 是默认字符集。
要点:
CREATE TABLE定义列、数据类型和约束;新建的表是空的。- 选择精确的数据类型;金额使用
DECIMAL。 PRIMARY KEY标识行;AUTO_INCREMENT生成键值。NOT NULL、UNIQUE、DEFAULT和CHECK保证数据有效。FOREIGN KEY关联表;ON DELETE决定子行的处理方式。- 表已存在时,
IF NOT EXISTS可以避免报错。
常见问题
如何在 SQL 中创建带主键的表?
给键列加上 PRIMARY KEY:CREATE TABLE students (student_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL);。对于由多列组成的键,在列列表之后写 PRIMARY KEY (col1, col2)。
PRIMARY KEY 和 UNIQUE 有什么区别?
两者都禁止重复。一张表只能有一个主键,且主键列不能为 NULL。一张表可以有多个 UNIQUE 约束,并且它们允许 NULL 值。
如何在 CREATE TABLE 中添加外键?
在列之后写 FOREIGN KEY (column) REFERENCES other_table (key_column),还可以加上 ON DELETE 和 ON UPDATE 动作。被引用的列必须是主键或具有唯一约束。