### 24.6.1分区键、主键和唯一键

[](<>)[](<>)[](<>)[](<>)[](<>)

本节讨论分区键与主键和唯一键的关系。控制这种关系的规则可以表示为：分区表的分区表达式中使用的所有列都必须是该表可能具有的每个唯一键的一部分。

换句话说，*表上的每个唯一键都必须使用表的分区表达式中的每一列*（这还包括表的主键，因为它定义为唯一键。本节后面将讨论这种特殊情况。）例如，以下每个表格创建语句都是无效的：

```
CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1),
    UNIQUE KEY (col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;
```

在每种情况下，建议的表都至少有一个唯一的键，该键不包括分区表达式中使用的所有列。

以下每一条语句都是有效的，并且代表了相应的无效表创建语句的一种工作方式：

```
CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2, col3)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;
```

此示例显示了在这种情况下产生的错误：

```
mysql> CREATE TABLE t3 (
    ->     col1 INT NOT NULL,
    ->     col2 DATE NOT NULL,
    ->     col3 INT NOT NULL,
    ->     col4 INT NOT NULL,
    ->     UNIQUE KEY (col1, col2),
    ->     UNIQUE KEY (col3)
    -> )
    -> PARTITION BY HASH(col1 + col3)
    -> PARTITIONS 4;
ERROR 1491 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
```

这个[`创建表格`](create-table.html)声明失败是因为`可乐1`和`可乐3`包含在建议的分区键中，但这两列都不是表上两个唯一键的一部分。这显示了无效表定义的一种可能修复方法：

```
mysql> CREATE TABLE t3 (
    ->     col1 INT NOT NULL,
    ->     col2 DATE NOT NULL,
    ->     col3 INT NOT NULL,
    ->     col4 INT NOT NULL,
    ->     UNIQUE KEY (col1, col2, col3),
    ->     UNIQUE KEY (col3)
    -> )
    -> PARTITION BY HASH(col3)
    -> PARTITIONS 4;
Query OK, 0 rows affected (0.05 sec)
```

在这种情况下，建议的分区键`可乐3`是两个唯一键的一部分，并且表创建语句成功。

下表根本无法分区，因为无法在分区键中包含属于两个唯一键的任何列：

```
CREATE TABLE t4 (
    col1 INT NOT NULL,
    col2 INT NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3),
    UNIQUE KEY (col2, col4)
);
```

由于根据定义，每个主键都是唯一的键，因此此限制还包括表的主键（如果有）。例如，下面两条语句无效：

```
CREATE TABLE t5 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t6 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col3),
    UNIQUE KEY(col2)
)
PARTITION BY HASH( YEAR(col2) )
PARTITIONS 4;
```

在这两种情况下，主键并不包括分区表达式中引用的所有列。但是，以下两种说法都是有效的：

```
CREATE TABLE t7 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2)
)
PARTITION BY HASH(col1 + YEAR(col2))
PARTITIONS 4;

CREATE TABLE t8 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2, col4),
    UNIQUE KEY(col2, col1)
)
PARTITION BY HASH(col1 + YEAR(col2))
PARTITIONS 4;
```

如果一个表没有唯一键，这包括没有主键，那么这个限制就不适用，只要列类型与分区类型兼容，就可以在分区表达式中使用任何列。

出于同样的原因，以后不能向分区表添加唯一键，除非该键包含表的分区表达式使用的所有列。考虑如下所示创建的分区表：

```
mysql> CREATE TABLE t_no_pk (c1 INT, c2 INT)
    ->     PARTITION BY RANGE(c1) (
    ->         PARTITION p0 VALUES LESS THAN (10),
    ->         PARTITION p1 VALUES LESS THAN (20),
    ->         PARTITION p2 VALUES LESS THAN (30),
    ->         PARTITION p3 VALUES LESS THAN (40)
    ->     );
Query OK, 0 rows affected (0.12 sec)
```

可以将主键添加到`t_no_pk`使用这两种方法中的任何一种[`改变桌子`](alter-table-partition-operations.html)声明：

```
#  possible PK
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c1);
Query OK, 0 rows affected (0.13 sec)
Records: 0  Duplicates: 0  Warnings: 0

# drop this PK
mysql> ALTER TABLE t_no_pk DROP PRIMARY KEY;
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

#  use another possible PK
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c1, c2);
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

# drop this PK
mysql> ALTER TABLE t_no_pk DROP PRIMARY KEY;
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0
```

然而，下一个声明失败了，因为`c1`是分区键的一部分，但不是建议的主键的一部分：

```
#  fails with error 1503
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c2);
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
```

自从`t_no_pk`只有`c1`在其分区表达式中，尝试在`c2`单靠自己是失败的。但是，您可以添加一个同时使用这两种方法的唯一密钥`c1`和`c2`.

这些规则也适用于要使用分区的现有非分区表[`改变桌子。。。分割`](alter-table-partition-operations.html)考虑一张桌子`np_pk`创建如下所示：

```
mysql> CREATE TABLE np_pk (
    ->     id INT NOT NULL AUTO_INCREMENT,
    ->     name VARCHAR(50),
    ->     added DATE,
    ->     PRIMARY KEY (id)
    -> );
Query OK, 0 rows affected (0.08 sec)
```

下面[`改变桌子`](alter-table-partition-operations.html)语句失败并出现错误，因为`补充`列不是表中任何唯一键的一部分：

```
mysql> ALTER TABLE np_pk
    ->     PARTITION BY HASH( TO_DAYS(added) )
    ->     PARTITIONS 4;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
```

然而，使用`身份证件`分区列的列是有效的，如下所示：

```
mysql> ALTER TABLE np_pk
    ->     PARTITION BY HASH(id)
    ->     PARTITIONS 4;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0
```

就`np_pk`，唯一可以用作分区表达式一部分的列是`身份证件`; 如果希望使用分区表达式中的任何其他列对该表进行分区，则必须首先修改该表，方法是将所需的一列或多列添加到主键，或完全删除主键。
