MySQL 中的分区并不会禁止 NULL 作为分区表达式的值,无论它是列值还是用户提供的表达式值。尽管允许将 NULL 用作必须生成整数的表达式的值,但需要记住 NULL 并不是数字。MySQL 的分区实现将 NULL 视为小于任何非 NULL 的值,就像 ORDER BY 所做的那样。Partitioning in MySQL does nothing to disallow
NULL as the value of a partitioning
expression, whether it is a column value or the value of a
user-supplied expression. Even though it is permitted to use
NULL as the value of an expression that must
otherwise yield an integer, it is important to keep in mind that
NULL is not a number. MySQL's
partitioning implementation treats NULL as
being less than any non-NULL value, just as
ORDER BY does.
这意味着对 NULL 的处理在不同类型的分区之间有所不同,如果你没有准备好,可能会产生你意想不到的行为。在这种情况下,我们在本节中讨论每种 MySQL 分区类型在确定一行应该存储在哪个分区时如何处理 NULL 值,并为每种情况提供示例。This means that treatment of NULL varies
between partitioning of different types, and may produce
behavior which you do not expect if you are not prepared for it.
This being the case, we discuss in this section how each MySQL
partitioning type handles NULL values when
determining the partition in which a row should be stored, and
provide examples for each.
使用 RANGE 分区处理 NULL。 如果你向按 RANGE 分区的表中插入一行,而用于确定分区的列值为 NULL,该行会被插入到最低的分区。考虑在名为 p 的数据库中创建的以下两个表:Handling of NULL with RANGE partitioning.
If you insert a row into a table partitioned by
RANGE such that the column value used to
determine the partition is NULL, the row is
inserted into the lowest partition. Consider these two tables
in a database named p, created as follows:
mysql>CREATE TABLE t1 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY RANGE(c1) (->PARTITION p0 VALUES LESS THAN (0),->PARTITION p1 VALUES LESS THAN (10),->PARTITION p2 VALUES LESS THAN MAXVALUE->);Query OK, 0 rows affected (0.09 sec) mysql>CREATE TABLE t2 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY RANGE(c1) (->PARTITION p0 VALUES LESS THAN (-5),->PARTITION p1 VALUES LESS THAN (0),->PARTITION p2 VALUES LESS THAN (10),->PARTITION p3 VALUES LESS THAN MAXVALUE->);Query OK, 0 rows affected (0.09 sec)
您可以使用以下查询,在 INFORMATION_SCHEMA 数据库中的 PARTITIONS 表上查看由这两个 CREATE TABLE 语句创建的分区:You can see the partitions created by these two
CREATE TABLE statements using the
following query against the
PARTITIONS table in the
INFORMATION_SCHEMA database:
mysql>SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH>FROM INFORMATION_SCHEMA.PARTITIONS>WHERE TABLE_SCHEMA = 'p' AND TABLE_NAME LIKE 't_';+------------+----------------+------------+----------------+-------------+ | TABLE_NAME | PARTITION_NAME | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | +------------+----------------+------------+----------------+-------------+ | t1 | p0 | 0 | 0 | 0 | | t1 | p1 | 0 | 0 | 0 | | t1 | p2 | 0 | 0 | 0 | | t2 | p0 | 0 | 0 | 0 | | t2 | p1 | 0 | 0 | 0 | | t2 | p2 | 0 | 0 | 0 | | t2 | p3 | 0 | 0 | 0 | +------------+----------------+------------+----------------+-------------+ 7 rows in set (0.00 sec)
(有关此表的更多信息,请参见 第 28.3.21 节,“INFORMATION_SCHEMA PARTITIONS 表”。)现在让我们向每个表中插入一行,该行在用作分区键的列中包含 NULL,并使用两条 SELECT 语句验证行是否已插入:(For more information about this table, see
Section 28.3.21, “The INFORMATION_SCHEMA PARTITIONS Table”.) Now let
us populate each of these tables with a single row containing a
NULL in the column used as the partitioning
key, and verify that the rows were inserted using a pair of
SELECT statements:
mysql>INSERT INTO t1 VALUES (NULL, 'mothra');Query OK, 1 row affected (0.00 sec) mysql>INSERT INTO t2 VALUES (NULL, 'mothra');Query OK, 1 row affected (0.00 sec) mysql>SELECT * FROM t1;+------+--------+ | id | name | +------+--------+ | NULL | mothra | +------+--------+ 1 row in set (0.00 sec) mysql>SELECT * FROM t2;+------+--------+ | id | name | +------+--------+ | NULL | mothra | +------+--------+ 1 row in set (0.00 sec)
您可以通过针对 INFORMATION_SCHEMA.PARTITIONS 重新运行之前的查询并检查输出,来查看哪些分区用于存储插入的行:You can see which partitions are used to store the inserted rows
by rerunning the previous query against
INFORMATION_SCHEMA.PARTITIONS and
inspecting the output:
mysql>SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH>FROM INFORMATION_SCHEMA.PARTITIONS>WHERE TABLE_SCHEMA = 'p' AND TABLE_NAME LIKE 't_';+------------+----------------+------------+----------------+-------------+ | TABLE_NAME | PARTITION_NAME | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | +------------+----------------+------------+----------------+-------------+ | t1 | p0 | 1 | 20 | 20 | | t1 | p1 | 0 | 0 | 0 | | t1 | p2 | 0 | 0 | 0 | | t2 | p0 | 1 | 20 | 20 | | t2 | p1 | 0 | 0 | 0 | | t2 | p2 | 0 | 0 | 0 | | t2 | p3 | 0 | 0 | 0 | +------------+----------------+------------+----------------+-------------+ 7 rows in set (0.01 sec)
您还可以通过删除这些分区,然后重新运行SELECT语句来证明这些行存储在每个表的编号最低的分区中:You can also demonstrate that these rows were stored in the
lowest-numbered partition of each table by dropping these
partitions, and then re-running the
SELECT statements:
mysql>ALTER TABLE t1 DROP PARTITION p0;Query OK, 0 rows affected (0.16 sec) mysql>ALTER TABLE t2 DROP PARTITION p0;Query OK, 0 rows affected (0.16 sec) mysql>SELECT * FROM t1;Empty set (0.00 sec) mysql>SELECT * FROM t2;Empty set (0.00 sec)
(有关ALTER TABLE ... DROP
PARTITION的更多信息,请参见第 15.1.9 节,“ALTER TABLE 语句”。)(For more information on ALTER TABLE ... DROP
PARTITION, see Section 15.1.9, “ALTER TABLE Statement”.)
NULL对于使用 SQL 函数的分区表达式也以这种方式处理。假设我们使用如下的CREATE
TABLE语句定义一个表:NULL is also treated in this way for
partitioning expressions that use SQL functions. Suppose that we
define a table using a CREATE
TABLE statement such as this one:
CREATE TABLE tndate (
id INT,
dt DATE
)
PARTITION BY RANGE( YEAR(dt) ) (
PARTITION p0 VALUES LESS THAN (1990),
PARTITION p1 VALUES LESS THAN (2000),
PARTITION p2 VALUES LESS THAN MAXVALUE
);
与其他 MySQL 函数一样,YEAR(NULL) 返回 NULL。具有 dt 列值为 NULL 的行被视为分区表达式计算结果小于任何其他值,因此会插入到分区 p0 中。As with other MySQL functions,
YEAR(NULL) returns
NULL. A row with a dt
column value of NULL is treated as though the
partitioning expression evaluated to a value less than any other
value, and so is inserted into partition p0.
LIST 分区中对 NULL 的处理。 一个使用 LIST 分区的表,如果且仅如果它的某个分区是使用包含 NULL 的值列表定义的,才允许 NULL 值。相反,如果一个使用 LIST 分区的表在值列表中没有明确使用 NULL,则会拒绝分区表达式结果为 NULL 的行,如下例所示:Handling of NULL with LIST partitioning.
A table that is partitioned by LIST admits
NULL values if and only if one of its
partitions is defined using that value-list that contains
NULL. The converse of this is that a table
partitioned by LIST which does not
explicitly use NULL in a value list rejects
rows resulting in a NULL value for the
partitioning expression, as shown in this example:
mysql>CREATE TABLE ts1 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY LIST(c1) (->PARTITION p0 VALUES IN (0, 3, 6),->PARTITION p1 VALUES IN (1, 4, 7),->PARTITION p2 VALUES IN (2, 5, 8)->);Query OK, 0 rows affected (0.01 sec) mysql>INSERT INTO ts1 VALUES (9, 'mothra');ERROR 1504 (HY000): Table has no partition for value 9 mysql>INSERT INTO ts1 VALUES (NULL, 'mothra');ERROR 1504 (HY000): Table has no partition for value NULL
只有 c1 值在 0 到 8(含)之间的行才能插入到 ts1 中。NULL 落在此范围之外,就像数字 9 一样。我们可以创建表 ts2 和 ts3,它们的值列表包含 NULL,如下所示:Only rows having a c1 value between
0 and 8 inclusive can be
inserted into ts1. NULL
falls outside this range, just like the number
9. We can create tables
ts2 and ts3 having value
lists containing NULL, as shown here:
mysql>CREATE TABLE ts2 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY LIST(c1) (->PARTITION p0 VALUES IN (0, 3, 6),->PARTITION p1 VALUES IN (1, 4, 7),->PARTITION p2 VALUES IN (2, 5, 8),->PARTITION p3 VALUES IN (NULL)->);Query OK, 0 rows affected (0.01 sec) mysql>CREATE TABLE ts3 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY LIST(c1) (->PARTITION p0 VALUES IN (0, 3, 6),->PARTITION p1 VALUES IN (1, 4, 7, NULL),->PARTITION p2 VALUES IN (2, 5, 8)->);Query OK, 0 rows affected (0.01 sec)
在为分区定义值列表时,你可以(而且应该)将 NULL 当作其他值一样对待。例如,VALUES IN (NULL) 和 VALUES IN (1, 4, 7, NULL) 都是有效的,VALUES IN (1, NULL, 4, 7)、VALUES IN
(NULL, 1, 4, 7) 等也同样有效。你可以将在列 c1 中为 NULL 的行插入到每个表 ts2 和 ts3 中:When defining value lists for partitioning, you can (and should)
treat NULL just as you would any other value.
For example, both VALUES IN (NULL) and
VALUES IN (1, 4, 7, NULL) are valid, as are
VALUES IN (1, NULL, 4, 7), VALUES IN
(NULL, 1, 4, 7), and so on. You can insert a row
having NULL for column c1
into each of the tables ts2 and
ts3:
mysql>INSERT INTO ts2 VALUES (NULL, 'mothra');Query OK, 1 row affected (0.00 sec) mysql>INSERT INTO ts3 VALUES (NULL, 'mothra');Query OK, 1 row affected (0.00 sec)
通过对 INFORMATION_SCHEMA.PARTITIONS 执行适当的查询,你可以确定用于存储刚插入行的分区(我们假设,与前面的示例一样,分区表是在 p 数据库中创建的):By issuing the appropriate query against
INFORMATION_SCHEMA.PARTITIONS, you
can determine which partitions were used to store the rows just
inserted (we assume, as in the previous examples, that the
partitioned tables were created in the p
database):
mysql>SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH>FROM INFORMATION_SCHEMA.PARTITIONS>WHERE TABLE_SCHEMA = 'p' AND TABLE_NAME LIKE 'ts_';+------------+----------------+------------+----------------+-------------+ | TABLE_NAME | PARTITION_NAME | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | +------------+----------------+------------+----------------+-------------+ | ts2 | p0 | 0 | 0 | 0 | | ts2 | p1 | 0 | 0 | 0 | | ts2 | p2 | 0 | 0 | 0 | | ts2 | p3 | 1 | 20 | 20 | | ts3 | p0 | 0 | 0 | 0 | | ts3 | p1 | 1 | 20 | 20 | | ts3 | p2 | 0 | 0 | 0 | +------------+----------------+------------+----------------+-------------+ 7 rows in set (0.01 sec)
如本节之前所示,你还可以通过删除这些分区然后执行 SELECT 来验证用于存储行的分区。As shown earlier in this section, you can also verify which
partitions were used for storing the rows by deleting these
partitions and then performing a
SELECT.
使用 HASH 和 KEY 分区时对 NULL 的处理。 NULL 对按 HASH 或 KEY 分区的表的处理略有不同。在这些情况下,任何返回 NULL 值的分区表达式都被视为其返回值为零。我们可以通过检查在创建按 HASH 分区的表并向其插入包含适当值的记录时对文件系统的影响来验证这种行为。假设你有一个表 th(同样在 p 数据库中)使用以下语句创建:Handling of NULL with HASH and KEY partitioning.
NULL is handled somewhat differently for
tables partitioned by HASH or
KEY. In these cases, any partition
expression that yields a NULL value is
treated as though its return value were zero. We can verify
this behavior by examining the effects on the file system of
creating a table partitioned by HASH and
populating it with a record containing appropriate values.
Suppose that you have a table th (also in
the p database) created using the following
statement:
mysql>CREATE TABLE th (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY HASH(c1)->PARTITIONS 2;Query OK, 0 rows affected (0.00 sec)
可以使用此处显示的查询查看属于该表的分区:The partitions belonging to this table can be viewed using the query shown here:
mysql> SELECT TABLE_NAME,PARTITION_NAME,TABLE_ROWS,AVG_ROW_LENGTH,DATA_LENGTH
> FROM INFORMATION_SCHEMA.PARTITIONS
> WHERE TABLE_SCHEMA = 'p' AND TABLE_NAME ='th';
+------------+----------------+------------+----------------+-------------+
| TABLE_NAME | PARTITION_NAME | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH |
+------------+----------------+------------+----------------+-------------+
| th | p0 | 0 | 0 | 0 |
| th | p1 | 0 | 0 | 0 |
+------------+----------------+------------+----------------+-------------+
2 rows in set (0.00 sec)
TABLE_ROWS 为 0。现在向 th 中插入两行,其 c1 列的值分别为 NULL 和 0,并验证这些行已被插入,如下所示:TABLE_ROWS for each partition is 0. Now
insert two rows into th whose
c1 column values are NULL
and 0, and verify that these rows were inserted, as shown here:
mysql>INSERT INTO th VALUES (NULL, 'mothra'), (0, 'gigan');Query OK, 1 row affected (0.00 sec) mysql>SELECT * FROM th;+------+---------+ | c1 | c2 | +------+---------+ | NULL | mothra | +------+---------+ | 0 | gigan | +------+---------+ 2 rows in set (0.01 sec)
请记住,对于任何整数N,NULL MOD
N的值总是NULL。对于按HASH或KEY进行分区的表,此结果在确定正确分区时被视为0。再次检查信息模式PARTITIONS表,我们可以看到两行都被插入到了分区p0:Recall that for any integer N, the
value of NULL MOD
is always
NNULL. For tables that are partitioned by
HASH or KEY, this result
is treated for determining the correct partition as
0. Checking the Information Schema
PARTITIONS table once again, we can
see that both rows were inserted into partition
p0:
mysql>SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH>FROM INFORMATION_SCHEMA.PARTITIONS>WHERE TABLE_SCHEMA = 'p' AND TABLE_NAME ='th';+------------+----------------+------------+----------------+-------------+ | TABLE_NAME | PARTITION_NAME | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | +------------+----------------+------------+----------------+-------------+ | th | p0 | 2 | 20 | 20 | | th | p1 | 0 | 0 | 0 | +------------+----------------+------------+----------------+-------------+ 2 rows in set (0.00 sec)
通过重复上一个例子,并在表的定义中使用PARTITION BY
KEY替代PARTITION BY HASH,你可以验证NULL在这种类型的分区中也被视为 0。By repeating the last example using PARTITION BY
KEY in place of PARTITION BY HASH
in the definition of the table, you can verify that
NULL is also treated like 0 for this type of
partitioning.