#### 17.5.1.9 在源和副本上具有不同表定义的复制

[](<>)

用于复制的源表和目标表不必相同。源上的表可以比副本的表副本具有更多或更少的列。此外，源和副本上的对应表列可以使用不同的数据类型，但必须符合一定的条件。

笔记

不支持分区不同的表之间的复制。看[第 17.5.1.24 节，“复制和分区”](replication-features-partitioning.html).

在源表和目标表没有相同定义的所有情况下，源表和副本上的数据库和表名必须相同。以下两节将通过示例讨论其他条件。

##### 17.5.1.9.1 在源或副本上具有更多列的复制

您可以将表从源复制到副本，以使表的源副本和副本副本具有不同的列数，但需满足以下条件：

-   两个版本的表共有的列必须在源和副本上以相同的顺序定义。（即使两个表具有相同的列数也是如此。）

-   两个版本的表共有的列必须在任何其他列之前定义。

    这意味着执行一个[`更改表`](alter-table.html)在副本上的语句中，如果在两个表共有的列范围内将新列插入到表中，则会导致复制失败，如下例所示：

    假设一个表`吨`，存在于源和副本上，由以下定义[`创建表`](create-table.html)陈述：

    ```
    CREATE TABLE t (
        c1 INT,
        c2 INT,
        c3 INT
    );
    ```

    假设[`更改表`](alter-table.html)此处显示的语句在副本上执行：

    ```
    ALTER TABLE t ADD COLUMN cnew1 INT AFTER c3;
    ```

    以前的[`更改表`](alter-table.html)在副本上是允许的，因为列`c1`,`c2`， 和`c3`两个版本的表共有的`吨`在表的两个版本中保持分组在一起，在任何不同的列之前。

    然而，以下[`更改表`](alter-table.html)语句不能在副本上执行而不会导致复制中断：

    ```
    ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;
    ```

    在复制的副本上执行后复制失败[`更改表`](alter-table.html)刚刚显示的语句，因为新列`cnew2`位于两个版本共有的列之间`吨`.

-   具有更多列的表版本中的每个“额外”列都必须具有默认值。

    列的默认值由许多因素决定，包括其类型、是否使用`默认`选项，是否声明为`空值`，以及创建时生效的服务器 SQL 模式；有关详细信息，请参阅[第 11.6 节，“数据类型默认值”](data-type-defaults.html)）。

    此外，当表的副本副本的列数多于源副本的列数时，表的公用列必须在两个表中使用相同的数据类型。

**例子。**以下示例说明了一些有效和无效的表定义：

**更多关于源的列。**下表定义有效且复制正确：

```
source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT);
```

以下表定义会引发错误，因为两个版本的表共有的列的定义在副本上的顺序与在源上的顺序不同：

```
source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica>  CREATE TABLE t1 (c2 INT, c1 INT);
```

以下表定义也会引发错误，因为源上额外列的定义出现在表的两个版本共有的列定义之前：

```
source> CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT);
```

**副本上的更多列。**下表定义有效且复制正确：

```
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
```

以下定义会引发错误，因为表的两个版本共有的列在源和副本上的定义顺序不同：

```
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c2 INT, c1 INT, c3 INT);
```

以下表定义也会引发错误，因为副本版本表中额外列的定义出现在表的两个版本共有的列定义之前：

```
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
```

以下表定义失败，因为与源版本相比，表的副本版本具有额外的列，并且表的两个版本对公共列使用不同的数据类型`c2`：

```
source> CREATE TABLE t1 (c1 INT, c2 BIGINT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
```

##### 17.5.1.9.2 复制具有不同数据类型的列

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

理想情况下，同一表的源副本和副本副本上的相应列应具有相同的数据类型。但是，只要满足某些条件，这并不总是严格执行。

通常可以从给定数据类型的列复制到相同类型和相同大小或宽度（如果适用或更大）的另一列。例如，您可以从`字符(10)`列到另一个`字符(10)`，或从`字符(10)`列到一个`字符(25)`列没有任何问题。在某些情况下，还可以从具有一种数据类型的列（在源上）复制到具有不同数据类型的列（在副本上）；当列的源版本的数据类型在副本上提升为相同大小或更大的类型时，这称为属性提升。

属性提升可用于基于语句和基于行的复制，并且不依赖于源或副本使用的存储引擎。但是，日志格式的选择确实会影响允许的类型转换。具体细节将在本节后面讨论。

重要的

无论您使用基于语句的复制还是基于行的复制，如果您希望使用属性提升，表的副本副本不能包含比源副本更多的列。

**基于语句的复制。**当使用基于语句的复制时，一个简单的经验法则是，“如果在源上运行的语句也会在副本上成功执行，那么它也应该成功复制”。换句话说，如果语句使用与副本上给定列的类型兼容的值，则可以复制该语句。例如，您可以插入任何适合`小音`列成一个`大整数`列也是；随之而来的是，即使您更改 a 的类型`小音`表的副本副本中的列`大整数`，在源上成功的任何插入该列也应该在副本上成功，因为不可能有合法的`小音`值大到足以超过`大整数`柱子。

[](<>)**基于行的复制：属性提升和降级。**基于行的复制支持较小数据类型和较大类型之间的属性提升和降级。还可以指定是否允许降级列值的有损（截断）或无损转换，如本节后面所述。

**有损和无损转换。**如果目标类型不能代表插入的值，则必须决定如何处理转换。如果我们允许转换但截断（或以其他方式修改）源值以在目标列中实现“拟合”，我们会进行所谓的有损转换。不需要截断或类似修改以适合目标列中的源列值的转换是无损转换。

**类型转换模式。**系统变量的全局值`副本类型转换`（来自 MySQL 8.0.26）或`slave_type_conversions`（在 MySQL 8.0.26 之前）控制副本上使用的类型转换模式。此变量从以下列表中获取一组值，这些值描述了每种模式对副本类型转换行为的影响：

全部\_有损

在这种模式下，允许进行意味着信息丢失的类型转换。

这并不意味着允许无损转换，只是只允许需要有损转换或根本不转换的情况；例如，启用*只要*这种模式允许`INT`要转换为的列`小音`（有损转换），但不是`小音`列到一个`INT`列（非有损）。在这种情况下尝试后一种转换将导致复制停止并在副本上出现错误。

全部\_非\_有损

此模式允许进行不需要截断或对源值进行其他特殊处理的转换；也就是说，它允许在目标类型的范围比源类型更宽的情况下进行转换。

设置此模式与是否允许有损转换无关；这是由控制的`ALL_LOSSY`模式。要是`ALL_NON_LOSSY`已设置，但未设置`ALL_LOSSY`，然后尝试进行会导致数据丢失的转换（例如`INT`到`小音`， 要么`字符(25)`到`VARCHAR(20)`) 导致副本因错误而停止。

全部\_有损，全部\_非\_有损

设置此模式时，允许所有支持的类型转换，无论它们是否是有损转换。

全部\_签

将提升的整数类型视为有符号值（默认行为）。

全部\_未签名

将提升的整数类型视为无符号值。

全部\_已签名，全部\_未签名

如果可能，将提升的整数类型视为有符号，否则视为无符号。

 [*空的*]

什么时候`副本类型转换`要么`slave_type_conversions`未设置，不允许属性提升或降级；这意味着源表和目标表中的所有列必须属于相同类型。

此模式为默认模式。

提升整数类型时，不会保留其符号性。默认情况下，副本将所有此类值视为已签名。您可以使用控制此行为`ALL_SIGNED`,`ALL_UNSIGNED`， 或两者。`ALL_SIGNED`告诉副本将所有提升的整数类型视为有符号；`ALL_UNSIGNED`指示它将这些视为无符号。如果可能，同时指定这两者会导致副本将该值视为有符号，否则将其视为无符号；它们的列出顺序并不重要。两者都不`ALL_SIGNED`也不`ALL_UNSIGNED`如果至少其中一项有任何影响`ALL_LOSSY`要么`ALL_NONLOSSY`也不用。

更改类型转换模式需要使用新的重新启动副本`副本类型转换`要么`slave_type_conversions`环境。

**支持的转换。**不同但相似的数据类型之间支持的转换如下表所示：

-   在任何整数类型之间[`小音`](integer-types.html),[`小灵通`](integer-types.html),[`中型`](integer-types.html),[`INT`](integer-types.html)， 和[`大整数`](integer-types.html).

    这包括这些类型的有符号和无符号版本之间的转换。

    通过将源值截断为目标列允许的最大值（或最小值）来进行有损转换。为了确保从无符号类型到有符号类型时的无损转换，目标列必须足够大以容纳源列中的值范围。例如，您可以降级`TINYINT 未签名`无损地`小灵通`, 但不是`小音`.

-   在任何十进制类型之间[`十进制`](fixed-point-types.html),[`漂浮`](floating-point-types.html),[`双倍的`](floating-point-types.html)， 和[`数字`](fixed-point-types.html).

    `漂浮`到`双倍的`是无损转换；`双倍的`到`漂浮`只能有损处理。从转换`十进制（*`米`*,*`D`*)`到`十进制（*`米'`*,*`D'`*)`在哪里`*`D'`* >= *`D`*`和`(*`米'`*-*`D'`*) >= (*`米`*-*`D`*`) 是无损的；对于任何情况`*`米'`* < *`米`*`,`*`D'`* < *`D`*`，或两者兼而有之，只能进行有损转换。

    对于任何十进制类型，如果要存储的值不能适合目标类型，则根据文档中其他地方为服务器定义的舍入规则向下舍入该值。看[第 12.25.4 节，“舍入行为”](precision-math-rounding.html), 有关如何对小数类型执行此操作的信息。

-   在任何字符串类型之间[`字符`](char.html),[`VARCHAR`](char.html)， 和[`文本`](blob.html)，包括不同宽度之间的转换。

    转换`字符`,`VARCHAR`， 要么`文本`到一个`字符`,`VARCHAR`， 要么`文本`相同大小或更大的列永远不会有损。通过仅插入第一个来处理有损转换*`ñ`*副本上字符串的字符，其中*`ñ`*是目标列的宽度。

    重要的

    不支持使用不同字符集的列之间的复制。

-   在任何二进制数据类型之间[`二进制`](binary-varbinary.html),[`变量`](binary-varbinary.html)， 和[`斑点`](blob.html)，包括不同宽度之间的转换。

    转换`二进制`,`变量`， 要么`斑点`到一个`二进制`,`变量`， 要么`斑点`相同大小或更大的列永远不会有损。通过仅插入第一个来处理有损转换*`ñ`*副本上字符串的字节数，其中*`ñ`*是目标列的宽度。

-   任意 2 之间[`少量`](bit-type.html)任意 2 种尺寸的列。

    插入一个值时`少量（*`米`*)`列成一个`少量（*`米'`*)`列，其中`*`米'`* > *`米`*`, 的最高有效位`少量（*`米'`*)`列被清除（设置为零）和*`米`*的位`少量（*`米`*)`值被设置为的最低有效位`少量（*`米'`*)`柱子。

    从源插入值时`少量（*`米`*)`列成目标`少量（*`米'`*)`列，其中`*`米'`* < *`米`*`, 的最大可能值`少量（*`米'`*)`列已分配；换句话说，将“全部设置”值分配给目标列。

    不允许在上一个列表中没有的类型之间进行转换。
