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. Beginning version
5.1.8, MySQL Partitioning treats NULL as
being less than any non-NULL value, just as
ORDER BY does.
Because of this, this 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 types handles NULL values when
determining the partition in which a row should be stored, and
provide examples for each.
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. For example, consider these
two tables, created and populated 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 t1 (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY RANGE(c1) (->PARTITION p0 VALUES LESS THAN (-5),->PARTITION p1 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>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)
You can see which partitions the rows are stored in by
inspecting the filesystem and comparing the sizes of the
.MYD files correpsonding to the partitions:
/var/lib/mysql/test> ls -l *.MYD
-rw-rw---- 1 mysql mysql 20 2006-03-10 03:27 t1#P#p0.MYD
-rw-rw---- 1 mysql mysql 0 2006-03-10 03:17 t1#P#p1.MYD
-rw-rw---- 1 mysql mysql 0 2006-03-10 03:17 t1#P#p2.MYD
-rw-rw---- 1 mysql mysql 20 2006-03-10 03:27 t2#P#p0.MYD
-rw-rw---- 1 mysql mysql 0 2006-03-10 03:17 t2#P#p1.MYD
-rw-rw---- 1 mysql mysql 0 2006-03-10 03:17 t2#P#p2.MYD
-rw-rw---- 1 mysql mysql 0 2006-03-10 03:17 t2#P#p3.MYD
(Partition files are named according to the format
,
so that table_name#P#partition_name.extensiont1#P#p0.MYD is the file in which
data belonging to partition p0 of table
t1 is stored.
Note: Prior to MySQL 5.1.5,
these files would have been named t1_p0.MYD
and t2_p0.MYD, respectively. See
Section C.1.13, “Changes in release 5.1.6 (01 February 2006)” and Bug#13437 for information
regarding how this change impacts upgrades.)
You can also demonstrate that these rows were stored in the
lowest partition of the 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)
(For more information on ALTER TABLE ... DROP
PARTITION, see Section 13.1.2, “ALTER TABLE Syntax”.)
Such treatment also holds true for partitioning expressions that use SQL functions. Suppose that we have a table 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
);
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.
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
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)
When defining value lists for partitioning, you can treat
NULL just as you would any other value, and
so VALUES IN (NULL) and VALUES IN
(1, 4, 7, NULL) are both 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)
By inspecting the filesystem, you can verify that the first of
these statements inserted a new row into partition
p3 of table ts2, and that
the second statement inserted a new row into partition
p1 of table ts3:
/var/lib/mysql/test>ls -l ts2*.MYD-rw-rw---- 1 mysql mysql 0 2006-03-10 10:35 ts2#P#p0.MYD -rw-rw---- 1 mysql mysql 0 2006-03-10 10:35 ts2#P#p1.MYD -rw-rw---- 1 mysql mysql 0 2006-03-10 10:35 ts2#P#p2.MYD -rw-rw---- 1 mysql mysql 20 2006-03-10 10:35 ts2#P#p3.MYD /var/lib/mysql/test>ls -l ts3*.MYD-rw-rw---- 1 mysql mysql 0 2006-03-10 10:36 ts3#P#p0.MYD -rw-rw---- 1 mysql mysql 20 2006-03-10 10:36 ts3#P#p1.MYD -rw-rw---- 1 mysql mysql 0 2006-03-10 10:36 ts3#P#p2.MYD
As in earlier examples, we assume the use of the
bash shell on a Unix operating system for
listing files; use whatever your platform provides in this
regard. For example, if you are using a DOS shell on a Windows
operating system, the equivalent for the last listing might be
obtained by running the command dir ts3*.MYD
in the directory C:\Program Files\MySQL\MySQL Server
5.1\data\test.
As shown earlier in this section, you can also verify which
partitions were used for storing the values by deleting them and
then performing a SELECT.
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 filesystem of creating a table
partitioned by HASH and populating it with a
record containing appropriate values. Suppose that you have a
table th, created in the
test database, using this statement:
mysql>CREATE TABLE th (->c1 INT,->c2 VARCHAR(20)->)->PARTITION BY HASH(c1)->PARTITIONS 2;Query OK, 0 rows affected (0.00 sec)
Assuming an RPM installation of MySQL on Linux, this statement
creates two .MYD files in
/var/lib/mysql/test, which can be viewed in
the bash shell as follows:
/var/lib/mysql/test> ls th*.MYD -l
-rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 th#P#p0.MYD
-rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 th#P#p1.MYD
Note that the size of each file is 0 bytes. Now insert a row
into th whose c1 column
value is NULL, and verify that this row was
inserted:
mysql>INSERT INTO th VALUES (NULL, 'mothra');Query OK, 1 row affected (0.00 sec) mysql>SELECT * FROM th;+------+---------+ | c1 | c2 | +------+---------+ | NULL | mothra | +------+---------+ 1 row in set (0.01 sec)
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. Returning to the system shell (still
assuming bash for this purpose), we can see
that the value was inserted into the first partition (named
p0 by default) by listing the data files once
again:
var/lib/mysql/test> ls *.MYD -l
-rw-rw---- 1 mysql mysql 20 2005-11-04 18:44 th#P#p0.MYD
-rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 th#P#p1.MYD
You can see that the INSERT statement
modified only the file th#P#p0.MYD
(increasing its size on disk), without affecting the other data
file.
Important: Prior to MySQL
5.1.8, RANGE partitioning treated a
partitioning expression value of NULL as a
zero with respect to determining placement (the only way to
circumvent this was to design tables so as not to allow nulls,
usually by declaring columns NOT NULL). If
you have a RANGE partitioning scheme that
depends on this earlier behavior, you will need to re-implement
it when upgrading to MySQL 5.1.8 or later.

User Comments
Add your own comment.