复制的源表和目标表不必完全相同。源表上的列可以比副本表上的列多或少。此外,源和副本上对应的表列可以使用不同的数据类型,但需满足某些条件。
不支持在分区方式不同的表之间进行复制。请参阅 第 19.5.1.24 节,“复制和分区”。
在源表和目标表的定义不完全相同的所有情况下,数据库和表名在源和副本上必须相同。以下两节将通过示例讨论其他条件。
您可以将表从源复制到副本,以便表的源副本和副本副本具有不同数量的列,但需满足以下条件:
两个版本的表共有的列必须在源和副本上以相同的顺序定义。(即使两个表具有相同数量的列也是如此。)
两个版本的表共有的列必须在任何其他列之前定义。
这意味着如果在副本上执行
ALTER TABLE
语句,并且在该语句中,新列被插入到两个表共有的列的范围内,则会导致复制失败,如下面的示例所示:假设源和副本上都存在一个表
t
,该表由以下CREATE TABLE
语句定义:CREATE TABLE t ( c1 INT, c2 INT, c3 INT );
假设在副本上执行此处显示的
ALTER TABLE
语句:ALTER TABLE t ADD COLUMN cnew1 INT AFTER c3;
允许在副本上执行之前的
ALTER TABLE
语句,因为两个版本的表t
共有的列c1
、c2
和c3
在两个版本的表中仍然保持在一起,位于任何不同的列之前。但是,以下
ALTER TABLE
语句无法在副本上执行,否则会导致复制中断:ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;
在副本上执行刚显示的
ALTER TABLE
语句后,复制将失败,因为新列cnew2
位于两个版本的t
共有的列之间。具有更多列的表版本中的每个“额外”列都必须具有默认值。
列的默认值由许多因素决定,包括其类型、是否使用
DEFAULT
选项定义、是否声明为NULL
,以及创建时生效的服务器 SQL 模式;有关更多信息,请参见 第 13.6 节“数据类型默认值”)。
此外,当副本中的表比源表包含更多列时,两个表中相同的每一列都必须使用相同的数据类型。
示例。 以下示例说明了一些有效和无效的表定义
源表上的列更多。 以下表定义有效且可以正确复制
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);
理想情况下,源表和副本表上同一表的对应列应具有相同的数据类型。但是,只要满足某些条件,就不一定会严格执行此操作。
通常可以从给定数据类型的列复制到相同类型和相同大小或宽度(如果适用)或更大的另一列。例如,您可以毫无问题地从 CHAR(10)
列复制到另一个 CHAR(10)
列,或者从 CHAR(10)
列复制到 CHAR(25)
列。在某些情况下,也可以从具有一种数据类型(在源表上)的列复制到具有不同数据类型(在副本表上)的列;当源表版本的列的数据类型提升为副本表上相同大小或更大的类型时,这称为 属性提升。
属性提升可与基于语句的复制和基于行的复制一起使用,并且不依赖于源表或副本表使用的存储引擎。但是,日志记录格式的选择确实会影响允许的类型转换;本节稍后将讨论具体细节。
无论使用基于语句的复制还是基于行的复制,如果您希望采用属性提升,则副本表的副本都不能包含比源表的副本更多的列。
基于语句的复制。 使用基于语句的复制时,要遵循的一个简单的经验法则是:“如果在源表上运行的语句也可以在副本表上成功执行,那么它也应该可以成功复制”。换句话说,如果语句使用的值与副本表上给定列的类型兼容,则可以复制该语句。例如,您可以将适合 TINYINT
列的任何值插入到 BIGINT
列中;因此,即使您将副本表副本中 TINYINT
列的类型更改为 BIGINT
,对源表上该列的任何成功插入也应该在副本表上成功,因为不可能有合法的 TINYINT
值大到超过 BIGINT
列。
基于行的复制:属性提升和降级。 基于行的复制支持较小数据类型和较大数据类型之间的属性提升和降级。还可以指定是否允许对降级的列值进行有损(截断)或无损转换,如本节稍后所述。
有损转换和无损转换。 如果目标类型无法表示要插入的值,则必须决定如何处理转换。如果我们允许转换,但截断(或以其他方式修改)源值以使其““适合””目标列,则我们进行的是 有损转换。不需要截断或类似修改即可将源列值放入目标列的转换是 无损 转换。
类型转换模式。 系统变量 replica_type_conversions
的全局值控制副本表上使用的类型转换模式。此变量采用以下列表中的一组值,这些值描述了每种模式对副本表类型转换行为的影响
- ALL_LOSSY
在此模式下,允许进行会导致信息丢失的类型转换。
这并不意味着允许无损转换,而仅仅是只允许需要有损转换或根本不需要转换的情况;例如,启用仅此模式允许将
INT
列转换为TINYINT
(有损转换),但不允许将TINYINT
列转换为INT
列(无损)。在这种情况下尝试后者转换将导致复制停止,并在副本表上出现错误。- ALL_NON_LOSSY
此模式允许不需要截断或对源值进行其他特殊处理的转换;也就是说,它允许目标类型的范围比源类型更广的转换。
设置此模式与是否允许有损转换无关;这是通过
ALL_LOSSY
模式控制的。如果仅设置了ALL_NON_LOSSY
,而未设置ALL_LOSSY
,则尝试进行会导致数据丢失的转换(例如,从INT
转换为TINYINT
,或从CHAR(25)
转换为VARCHAR(20)
)会导致副本表停止并出现错误。- ALL_LOSSY,ALL_NON_LOSSY
设置此模式时,无论是否为有损转换,都允许所有受支持的类型转换。
- ALL_SIGNED
将提升的整数类型视为有符号值(默认行为)。
- ALL_UNSIGNED
将提升的整数类型视为无符号值。
- ALL_SIGNED,ALL_UNSIGNED
如果可能,将提升的整数类型视为有符号,否则视为无符号。
- [空]
未设置
replica_type_conversions
时,不允许进行属性提升或降级;这意味着源表和目标表中的所有列都必须具有相同的类型。这是默认模式。
提升整数类型时,不会保留其符号。默认情况下,副本表将所有此类值视为有符号值。您可以使用 ALL_SIGNED
、ALL_UNSIGNED
或两者来控制此行为。ALL_SIGNED
告诉副本表将所有提升的整数类型都视为有符号;ALL_UNSIGNED
指示它将这些类型视为无符号。同时指定两者会导致副本表在可能的情况下将值视为有符号,否则将其视为无符号;它们列出的顺序无关紧要。如果 ALL_LOSSY
或 ALL_NONLOSSY
中至少有一个未使用,则 ALL_SIGNED
和 ALL_UNSIGNED
均不起作用。
更改类型转换模式需要使用新的 replica_type_conversions
设置重新启动副本表。
受支持的转换。 以下列表显示了不同但相似的数据类型之间受支持的转换
在任何整数类型之间:
TINYINT
、SMALLINT
、MEDIUMINT
、INT
和BIGINT
。这包括这些类型的有符号和无符号版本之间的转换。
有损转换是通过将源值截断为目标列允许的最大值(或最小值)来进行的。为了确保从无符号类型转换为有符号类型时进行无损转换,目标列必须足够大以容纳源列中的值范围。例如,您可以将
TINYINT UNSIGNED
无损降级为SMALLINT
,但不能降级为TINYINT
。在任何十进制类型之间:
DECIMAL
、FLOAT
、DOUBLE
和NUMERIC
。FLOAT
到DOUBLE
是无损转换;DOUBLE
到FLOAT
只能进行有损处理。从DECIMAL(
到M
,D
)DECIMAL(
的转换,其中M'
,D'
)
且D'
>=D
(
) 是无损的;对于M'
-D'
) >= (M
-D
、M'
<M
或两者兼有的任何情况,只能进行有损转换。D'
<D
对于任何十进制类型,如果要存储的值不适合目标类型,则根据文档中其他地方为服务器定义的舍入规则将该值舍入。有关如何对十进制类型执行此操作的信息,请参见 第 14.24.4 节“舍入行为”。
在任何字符串类型之间:
CHAR
、VARCHAR
和TEXT
,包括不同宽度之间的转换。将
CHAR
、VARCHAR
或TEXT
转换为相同大小或更大大小的CHAR
、VARCHAR
或TEXT
列永远不会有损。有损转换是通过仅在副本表上插入字符串的前N
个字符来处理的,其中N
是目标列的宽度。重要不支持使用不同字符集的列之间的复制。
在任何二进制数据类型
BINARY
、VARBINARY
和BLOB
之间,包括不同宽度之间的转换。将
BINARY
、VARBINARY
或BLOB
转换为相同大小或更大大小的BINARY
、VARBINARY
或BLOB
列永远不会丢失数据。丢失数据的转换通过仅插入字符串的前N
个字节来处理,其中N
是目标列的宽度。在任何 2 个任意大小的
BIT
列之间。将值从
BIT(
列插入到M
)BIT(
列时(其中M'
)
),M'
>M
BIT(
列的最高有效位将被清除(设置为零),并且M'
)BIT(
值的M
)M
位将设置为BIT(
列的最低有效位。M'
)将值从源
BIT(
列插入到目标M
)BIT(
列时(其中M'
)
),将分配M'
<M
BIT(
列的最大可能值;换句话说,将为目标列分配一个““全设置””值。M'
)
不允许在上一个列表中不存在的类型之间进行转换。