约束

    TiDB 支持的非空约束规则与 MySQL 支持的一致。例如:

    1. INSERT INTO users (id,age,last_login) VALUES (NULL,123,NOW());
    1. Query OK, 1 row affected (0.02 sec)
    1. INSERT INTO users (id,age,last_login) VALUES (NULL,NULL,NOW());
    1. ERROR 1048 (23000): Column 'age' cannot be null
    1. INSERT INTO users (id,age,last_login) VALUES (NULL,123,NULL);
    1. Query OK, 1 row affected (0.03 sec)
    • 第一条 INSERT 语句成功,因为对于定义为 AUTO_INCREMENT 的列,允许 NULL 作为其特殊值。TiDB 将为其分配下一个自动值。

    • 第二条 INSERT 语句失败,因为 age 列被定义为 NOT NULL

    • 第三条 INSERT 语句成功,因为 last_login 列没有被明确地指定为 NOT NULL。默认允许 NULL 值。

    TiDB 会解析并忽略 CHECK 约束。该行为与 MySQL 5.7 的相兼容。

    示例如下:

    1. DROP TABLE IF EXISTS users;
    2. CREATE TABLE users (
    3. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    4. username VARCHAR(60) NOT NULL,
    5. UNIQUE KEY (username),
    6. CONSTRAINT min_username_length CHECK (CHARACTER_LENGTH(username) >=4)
    7. );
    8. INSERT INTO users (username) VALUES ('a');
    9. SELECT * FROM users;
    1. DROP TABLE IF EXISTS users;
    2. CREATE TABLE users (
    3. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    4. username VARCHAR(60) NOT NULL,
    5. UNIQUE KEY (username)
    6. );
    7. INSERT INTO users (username) VALUES ('dave'), ('sarah'), ('bill');

    默认的悲观事务模式下:

    1. INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill');
    1. ERROR 1062 (23000): Duplicate entry 'bill' for key 'username'

    乐观事务模式下且 :

    1. BEGIN OPTIMISTIC;
    2. INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill');
    1. Query OK, 0 rows affected (0.00 sec)
    2. Query OK, 3 rows affected (0.00 sec)
    3. Records: 3 Duplicates: 0 Warnings: 0
    1. Query OK, 2 rows affected (0.00 sec)
    2. Records: 2 Duplicates: 0 Warnings: 0
    1. COMMIT;
    1. ERROR 1062 (23000): Duplicate entry 'bill' for key 'username'

    在乐观事务的示例中,唯一约束的检查推迟到事务提交时才进行。由于 bill 值已经存在,这一行为导致了重复键错误。

    你可通过设置 tidb_constraint_check_in_place1 停用此行为(该变量设置对悲观事务无效,悲观事务始终在语句执行时检查约束)。当 tidb_constraint_check_in_place 设置为 1 时,则会在执行语句时就对唯一约束进行检查。例如:

    1. DROP TABLE IF EXISTS users;
    2. CREATE TABLE users (
    3. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    4. username VARCHAR(60) NOT NULL,
    5. UNIQUE KEY (username)
    6. );
    7. INSERT INTO users (username) VALUES ('dave'), ('sarah'), ('bill');
    1. SET tidb_constraint_check_in_place = 1;
    1. Query OK, 0 rows affected (0.00 sec)
    1. BEGIN OPTIMISTIC;
    1. Query OK, 0 rows affected (0.00 sec)
    1. INSERT INTO users (username) VALUES ('jane'), ('chris'), ('bill');
    1. ERROR 1062 (23000): Duplicate entry 'bill' for key 'username'
    2. ..

    第一条 INSERT 语句导致了重复键错误。这会造成额外的网络通信开销,并可能降低插入操作的吞吐量。

    与 MySQL 行为一样,主键约束包含了唯一约束,即创建了主键约束相当于拥有了唯一约束。此外,TiDB 其他的主键约束规则也与 MySQL 相似。例如:

    1. CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY);
    1. Query OK, 0 rows affected (0.12 sec)
    1. ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead
    1. CREATE TABLE t3 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL PRIMARY KEY);
    1. ERROR 1068 (42000): Multiple primary key defined
    1. CREATE TABLE t4 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b));
      • t2 创建失败,因为定义为主键的列 不能允许 NULL 值。
      • t3 创建失败,因为一张表只能有一个主键。
      • t4 创建成功,因为虽然只能有一个主键,但 TiDB 支持定义一个多列组合作为复合主键。

      除上述规则外,TiDB 目前仅支持对 NONCLUSTERED 的主键进行添加和删除操作。例如:

      1. CREATE TABLE t5 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b) CLUSTERED);
      2. ALTER TABLE t5 DROP PRIMARY KEY;
      1. ERROR 8200 (HY000): Unsupported drop primary key when the table is using clustered index
      1. CREATE TABLE t5 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b) NONCLUSTERED);
      2. ALTER TABLE t5 DROP PRIMARY KEY;
      1. Query OK, 0 rows affected (0.10 sec)

      要了解关于 CLUSTERED 主键的详细信息,请参考聚簇索引

      TiDB 支持创建外键约束。例如:

      1. CREATE TABLE users (
      2. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
      3. doc JSON
      4. );
      5. CREATE TABLE orders (
      6. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
      7. user_id INT NOT NULL,
      8. doc JSON,
      9. FOREIGN KEY fk_user_id (user_id) REFERENCES users(id)
      10. );
      1. SELECT table_name, column_name, constraint_name, referenced_table_name, referenced_column_name
      2. FROM information_schema.key_column_usage WHERE table_name IN ('users', 'orders');
      1. +------------+-------------+-----------------+-----------------------+------------------------+
      2. | table_name | column_name | constraint_name | referenced_table_name | referenced_column_name |
      3. +------------+-------------+-----------------+-----------------------+------------------------+
      4. | users | id | PRIMARY | NULL | NULL |
      5. | orders | id | PRIMARY | NULL | NULL |
      6. | orders | user_id | fk_user_id | users | id |
      7. +------------+-------------+-----------------+-----------------------+------------------------+
      8. 3 rows in set (0.00 sec)

      TiDB 也支持使用 ALTER TABLE 命令来删除外键 (DROP FOREIGN KEY) 和添加外键 (ADD FOREIGN KEY):

        1. START TRANSACTION;
        2. COMMIT;