19.5.1.9 复制具有不同表定义的源和副本
源和目标表的复制不需要完全相同。源表可以有更多或更少的列,而副本表的复制也可以使用不同的数据类型,subject to certain conditions.
不同分区的表之间的复制不受支持。请参阅第19.5.1.24节,“复制和分区”.
在所有情况下,源和目标表的名称都必须相同。其他条件在以下两个部分中有讨论,带有示例。
19.5.1.9.1 复制具有更多列的源或副本
可以将表从源复制到副本,使得源和副本的表具有不同的列数,subject to the following conditions:
-
共有的列在源和副本的表中必须以相同的顺序定义,即使两个表具有相同的列数。
-
共有的列在源和副本的表中必须在定义之前定义额外的列。
这意味着,在副本上执行
ALTER TABLE
语句,插入新列到共有的列范围内的表中将导致复制失败,例如:假设表
CREATE TABLE
语句定义了表t: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在共有的列范围内,导致复制失败。 -
每个“extra”列在具有更多列的表版本中必须具有默认值。
一个列的默认值是由多个因素确定的,包括其类型、是否定义了
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);
19.5.1.9.2 复制具有不同数据类型的列
源表和复制表中的同一表的对应列理想情况下应该具有相同的数据类型。然而,这并不是总是严格强制执行的,只要满足某些条件。
通常情况下,可以从一个数据类型的列复制到另一个相同类型和相同大小或宽度的列,例如,可以从一个CHAR(10)
列复制到另一个CHAR(10)
列,或者从一个CHAR(10)
列复制到一个CHAR(25)
列没有问题。在某些情况下,也可以从一个数据类型的列复制到另一个不同的数据类型的列;当源表中的列数据类型被提升到与复制表中的相同大小或更大时,这被称为属性提升。
属性提升可以与语句基于的和行基于的复制结合使用,并且不依赖于源表或复制表使用的存储引擎。然而,日志格式的选择将影响允许的类型转换的细节;这些细节将在本节中讨论。
无论您使用语句基于的或行基于的复制,复制表中的表不能包含更多列于源表中的列,如果您想使用属性提升。
语句级别复制. 在使用语句级别复制时,遵循的一个简单规则是:“如果在源端执行的语句也可以在副本上执行成功”。换言之,如果语句使用的值与副本中的某个列的类型兼容,那么语句可以被复制。例如,可以将任何值插入一个TINYINT
列到一个BIGINT
列中,因为这意味着,即使在副本的表中将一个TINYINT
列的类型更改为BIGINT
,在源端插入该列的语句也应该在副本上成功,因为无法有一个合法的TINYINT
值大于一个BIGINT
列。
行级别复制:属性提升和降低. 行级别复制支持在较小数据类型和较大类型之间的属性提升和降低。还可以指定是否允许损失(截断)或非损失的列值转换,后续在本节中有详细解释。
损失和非损失转换. 如果目标类型不能表示要插入的值,需要做出决策。我们允许转换,但截断(或修改)源值以在目标列中““fit””,我们称之为损失转换。一个不需要截断或类似修改来将源列值调整到目标列的转换称为非损失转换。
类型转换模式. 系统变量replica_type_conversions
的全局值控制副本的类型转换模式。这個变量可以取以下列表中的值,这个列表描述了每种模式对副本类型转换行为的影响:
- ALL_LOSSY
-
在这个模式下,类型转换可能会导致信息丢失的转换被允许。
这并不意味着非损失转换被允许,只是说只有在需要损失转换或不转换的情况下允许转换;例如,启用only这个模式允许将
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
-
将推广的整数类型视为有符号值,如果可能,否则视为无符号值。
- [empty]
-
在
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
是目标列的宽度。Important在使用不同字符集的列之间进行复制不支持。
-
在任何二进制数据类型
BINARY
、VARBINARY
和BLOB
之间,包括不同宽度之间的转换。将
BINARY
、VARBINARY
或BLOB
类型转换为同样大小或更大的BINARY
、VARBINARY
或BLOB
列类型的转换从不丢失数据。丢失数据的转换是通过在复制服务器上插入字符串的前N
个字节来处理,其中N
是目标列的宽度。 -
在任何两个大小不同的
BIT
列之间。当将
BIT(
列中的值插入到M
)BIT(
列中,其中M'
)
,那么M'
>M
BIT(
列的最高位将被清除(设置为零),并将M'
)M
位的BIT(
值设置为M
)BIT(
列的最低位。M'
)当将源
BIT(
列中的值插入到目标M
)BIT(
列中,其中M'
)
,那么目标列将分配最大可能值,即将 “all-set” 值分配给目标列。M'
<M
之前列表中没有的类型之间的转换不允许。