diff --git a/constraints.md b/constraints.md index 061da8919d16..aae5e6717bb3 100644 --- a/constraints.md +++ b/constraints.md @@ -6,67 +6,7 @@ aliases: ['/docs-cn/dev/reference/sql/constraints/'] # 约束 -TiDB 支持的基本约束与 MySQL 的基本相同,但有以下区别: - -- 默认对唯一约束进行[惰性检查](/transaction-overview.md#事务的惰性检查)。通过在事务提交时再进行批量检查,TiDB 能够减少网络开销、提升性能。您可通过设置 `tidb_constraint_check_in_place` 为 `TRUE` 改变此行为。 - -- TiDB 支持创建外键约束,但不会在 DML 语句中对外键进行约束(即外键约束不生效)。 - -## 外键约束 - -目前,TiDB 支持创建外键。例如: - -```sql -CREATE TABLE users ( - id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, - doc JSON -); -CREATE TABLE orders ( - id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, - user_id INT NOT NULL, - doc JSON, - FOREIGN KEY fk_user_id (user_id) REFERENCES users(id) -); -``` - -{{< copyable "sql" >}} - -```sql -SELECT table_name, column_name, constraint_name, referenced_table_name, referenced_column_name -FROM information_schema.key_column_usage WHERE table_name IN ('users', 'orders'); -``` - -``` -+------------+-------------+-----------------+-----------------------+------------------------+ -| table_name | column_name | constraint_name | referenced_table_name | referenced_column_name | -+------------+-------------+-----------------+-----------------------+------------------------+ -| users | id | PRIMARY | NULL | NULL | -| orders | id | PRIMARY | NULL | NULL | -| orders | user_id | fk_user_id | users | id | -+------------+-------------+-----------------+-----------------------+------------------------+ -3 rows in set (0.00 sec) -``` - -TiDB 也支持使用 `ALTER TABLE` 命令来删除外键(`DROP FOREIGN KEY`)和添加外键(`ADD FOREIGN KEY`): - -{{< copyable "sql" >}} - -```sql -ALTER TABLE orders DROP FOREIGN KEY fk_user_id; -ALTER TABLE orders ADD FOREIGN KEY fk_user_id (user_id) REFERENCES users(id); -``` - -### 注意 - -* TiDB 支持外键是为了在将其他数据库迁移到 TiDB 时,不会因为此语法报错。但是,TiDB 不会在 DML 语句中对外键进行约束。例如,即使 `users` 表中不存在 `id=123` 的记录,下列事务也能提交成功: - - ``` - START TRANSACTION; - INSERT INTO orders (user_id, doc) VALUES (123, NULL); - COMMIT; - ``` - -* TiDB 在执行 `SHOW CREATE TABLE` 语句的结果中不显示外键信息。 +TiDB 支持的约束与 MySQL 的基本相同。 ## 非空约束 @@ -118,61 +58,67 @@ Query OK, 1 row affected (0.03 sec) * 第三条 `INSERT` 语句成功,因为 `last_login` 列没有被明确地指定为 `NOT NULL`。默认允许 `NULL` 值。 -## 主键约束 +## 唯一约束 -TiDB 支持的主键约束规则与 MySQL 支持的相似。例如: +在 TiDB 的乐观事务中,默认会对唯一约束进行[惰性检查](/transaction-overview.md#事务的惰性检查)。通过在事务提交时再进行批量检查,TiDB 能够减少网络开销、提升性能。例如: {{< copyable "sql" >}} ```sql -CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY); -``` - -``` -Query OK, 0 rows affected (0.12 sec) +DROP TABLE IF EXISTS users; +CREATE TABLE users ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(60) NOT NULL, + UNIQUE KEY (username) +); +INSERT INTO users (username) VALUES ('dave'), ('sarah'), ('bill'); ``` {{< copyable "sql" >}} ```sql -CREATE TABLE t2 (a INT NULL PRIMARY KEY); +START TRANSACTION; ``` ``` -ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead +Query OK, 0 rows affected (0.00 sec) ``` {{< copyable "sql" >}} ```sql -CREATE TABLE t3 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL PRIMARY KEY); +INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill'); ``` ``` -ERROR 1068 (42000): Multiple primary key defined +Query OK, 3 rows affected (0.00 sec) +Records: 3 Duplicates: 0 Warnings: 0 ``` {{< copyable "sql" >}} ```sql -CREATE TABLE t4 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)); +INSERT INTO users (username) VALUES ('steve'),('elizabeth'); ``` ``` -Query OK, 0 rows affected (0.10 sec) +Query OK, 2 rows affected (0.00 sec) +Records: 2 Duplicates: 0 Warnings: 0 ``` -* 表 `t2` 创建失败,因为定义为主键的列 `a` 不能允许 `NULL` 值。 -* 表 `t3` 创建失败,因为一张表只能有一个主键。 -* 表 `t4` 创建成功,因为虽然只能有一个主键,但 TiDB 支持定义一个多列组合作为复合主键。 +{{< copyable "sql" >}} -除上述规则外,TiDB 还强加了另一个限制,即一旦一张表创建成功,其主键就不能再改变。 +```sql +COMMIT; +``` -## 唯一约束 +``` +ERROR 1062 (23000): Duplicate entry 'bill' for key 'username' +``` -在 TiDB 中,默认会对唯一约束进行惰性检查。通过直到事务提交时才进行批量检查,TiDB 能够减少网络通信开销。例如: +第一条 `INSERT` 语句不会导致重复键错误,这同 MySQL 的规则一致。该检查将推迟到事务提交时才会进行。 -{{< copyable "sql" >}} +你可通过设置 `tidb_constraint_check_in_place` 为 `1` 停用此行为(该变量设置对悲观事务无效,悲观事务始终在语句执行时检查约束)。如果停用此行为,则会在执行语句时就对唯一约束进行检查。例如: ```sql DROP TABLE IF EXISTS users; @@ -187,7 +133,7 @@ INSERT INTO users (username) VALUES ('dave'), ('sarah'), ('bill'); {{< copyable "sql" >}} ```sql -START TRANSACTION; +SET tidb_constraint_check_in_place = 1; ``` ``` @@ -197,78 +143,136 @@ Query OK, 0 rows affected (0.00 sec) {{< copyable "sql" >}} ```sql -INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill'); +START TRANSACTION; ``` ``` -Query OK, 3 rows affected (0.00 sec) -Records: 3 Duplicates: 0 Warnings: 0 +Query OK, 0 rows affected (0.00 sec) ``` {{< copyable "sql" >}} ```sql -INSERT INTO users (username) VALUES ('steve'),('elizabeth'); +INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill'); ``` ``` -Query OK, 2 rows affected (0.00 sec) -Records: 2 Duplicates: 0 Warnings: 0 +ERROR 1062 (23000): Duplicate entry 'bill' for key 'username' +.. ``` +第一条 `INSERT` 语句导致了重复键错误。这会造成额外的网络通信开销,并可能降低插入操作的吞吐量。 + +## 主键约束 + +与 MySQL 行为一样,主键约束包含了唯一约束,即创建了主键约束相当于拥有了唯一约束。此外,TiDB 其他的主键约束规则也与 MySQL 相似。例如: + {{< copyable "sql" >}} ```sql -COMMIT; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY); ``` ``` -ERROR 1062 (23000): Duplicate entry 'bill' for key 'username' +Query OK, 0 rows affected (0.12 sec) ``` -* 第一条 `INSERT` 语句不会导致重复键错误,这同 MySQL 的规则一致。该检查将推迟到事务提交时才会进行。 - -如果将 `tidb_constraint_check_in_place` 更改为 `TRUE`,则会在执行语句时就对唯一约束进行检查。例如: +{{< copyable "sql" >}} ```sql -DROP TABLE IF EXISTS users; -CREATE TABLE users ( - id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, - username VARCHAR(60) NOT NULL, - UNIQUE KEY (username) -); -INSERT INTO users (username) VALUES ('dave'), ('sarah'), ('bill'); +CREATE TABLE t2 (a INT NULL PRIMARY KEY); +``` + +``` +ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead ``` {{< copyable "sql" >}} ```sql -SET tidb_constraint_check_in_place = TRUE; +CREATE TABLE t3 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL PRIMARY KEY); ``` ``` -Query OK, 0 rows affected (0.00 sec) +ERROR 1068 (42000): Multiple primary key defined ``` {{< copyable "sql" >}} ```sql -START TRANSACTION; +CREATE TABLE t4 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)); ``` ``` -Query OK, 0 rows affected (0.00 sec) +Query OK, 0 rows affected (0.10 sec) +``` + +分析: + +* 表 `t2` 创建失败,因为定义为主键的列 `a` 不能允许 `NULL` 值。 +* 表 `t3` 创建失败,因为一张表只能有一个主键。 +* 表 `t4` 创建成功,因为虽然只能有一个主键,但 TiDB 支持定义一个多列组合作为复合主键。 + +除上述规则外,默认情况下,TiDB 还有一个额外限制,即一旦一张表创建成功,其主键就不能再改变。如果需要添加/删除主键,需要在 TiDB 配置文件中将 `alter-primary-key` 设置为 `true`,并重启 TiDB 实例使之生效。 + +当开启添加/删除主键功能以后,TiDB 允许对表添加/删除主键。但需要注意的是,对于在未开启该功能时创建的整数类型的主键的表,即使开启添加/删除主键功能,也不能删除其主键约束。 + +## 外键约束 + +> **注意:** +> +> TiDB 仅部分支持外键约束功能。 + +TiDB 支持创建外键约束。例如: + +```sql +CREATE TABLE users ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + doc JSON +); +CREATE TABLE orders ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + doc JSON, + FOREIGN KEY fk_user_id (user_id) REFERENCES users(id) +); ``` {{< copyable "sql" >}} ```sql -INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill'); +SELECT table_name, column_name, constraint_name, referenced_table_name, referenced_column_name +FROM information_schema.key_column_usage WHERE table_name IN ('users', 'orders'); ``` ``` -ERROR 1062 (23000): Duplicate entry 'bill' for key 'username' -.. ++------------+-------------+-----------------+-----------------------+------------------------+ +| table_name | column_name | constraint_name | referenced_table_name | referenced_column_name | ++------------+-------------+-----------------+-----------------------+------------------------+ +| users | id | PRIMARY | NULL | NULL | +| orders | id | PRIMARY | NULL | NULL | +| orders | user_id | fk_user_id | users | id | ++------------+-------------+-----------------+-----------------------+------------------------+ +3 rows in set (0.00 sec) +``` + +TiDB 也支持使用 `ALTER TABLE` 命令来删除外键(`DROP FOREIGN KEY`)和添加外键(`ADD FOREIGN KEY`): + +{{< copyable "sql" >}} + +```sql +ALTER TABLE orders DROP FOREIGN KEY fk_user_id; +ALTER TABLE orders ADD FOREIGN KEY fk_user_id (user_id) REFERENCES users(id); ``` -* 第一条 `INSERT` 语句导致了重复键错误。这会造成额外的网络通信开销,并可能降低插入操作的吞吐量。 +### 注意 + +* TiDB 支持外键是为了在将其他数据库迁移到 TiDB 时,不会因为此语法报错。但是,TiDB 不会在 DML 语句中对外键进行约束检查。例如,即使 `users` 表中不存在 `id=123` 的记录,下列事务也能提交成功: + + ``` + START TRANSACTION; + INSERT INTO orders (user_id, doc) VALUES (123, NULL); + COMMIT; + ``` + +* TiDB 在执行 `SHOW CREATE TABLE` 语句的结果中不显示外键信息。