MySQL的列表分区与范围分区在很多方面都很像,但是这两种分区类型主要的区别是,范围分区需要显示的定义一个连续不终端的范围分区,而列表分区是给每个分区定义一个列表,根据行中指定列的值是否存在于分区定义的列表中来判断该行属于哪个分区。
列表分区通过PARTITION BY LIST(expr)
来实现, expr是表中的列名后者一个表达式(这个表达式必须是基于列,返回值为int型), 通过VALUES IN (value_list)
来定义分区,value_list是一个用","
分开的int值的列表。
假设,我们有下面一张表:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
);
假设表中有20条数据,我们想把这些数据分成四组:
Region | Store ID Numbers |
---|---|
North | 3, 5, 6, 9, 17 |
East | 1, 2, 10, 11, 19, 20 |
West | 4, 12, 13, 14, 18 |
Central | 7, 8, 15, 16 |
我们使用下面的语句将表中的数据按照区域进行分区:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id)(
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION PCENTRAL VALUES IN (7,8,15,16)
);
这样我们可以很方便的添加、删除指定区域的员工记录。比如,将West区所有的店卖给另外一家公司。在这个区域所有店里的员工,我们可以用ALTER TABLE employees TRUNCATE PARTITION pWest
来删除指定区域所有店铺的员工,这条sql的效率比DELETE FROM employees WHERE store_id in (4,12,13,14,18)
要高的多。ALTER TABLE employees DROP PARTITION pWest
也可以删掉pWest区域所有的员工,但是也会将pWest
分区删掉。也可以使用ALTER TABLE employees ADD PARTITION
来新增一个分区。
跟范围分区的情况不同,列表分区没有类似MAXVALUE
的归总。所有希望出现的值都必须在表达式PARTITION ... VALUES IN (...)
里出现。当插入数据时,分区列值没有覆盖会报错:
mysql> CREATE TABLE h2 (
-> c1 INT,
-> c2 INT
-> )
-> PARTITION BY LIST(c1) (
-> PARTITION p0 VALUES IN (1, 4, 7),
-> PARTITION p1 VALUES IN (2, 5, 8)
-> );
Query OK, 0 rows affected (0.11 sec)
mysql> INSERT INTO h2 VALUES (3, 5);
ERROR 1525 (HY000): Table has no partition for value 3
当用一条sql插入多行时,插入的行为依赖是否使用了支持事务的引擎。以InnoDB为例,这条sql会被认为是单个事务,如果有一行数据没有覆盖,所有的行都不会插入成功;如果使用了MyISAM,在没有别覆盖之前的行都会被插入,之后的都不会被插入。
我们可以使用IGNORE
关键字来忽略错误,覆盖的所有行都会被插入,没有覆盖的行不会被插入。
mysql> TRUNCATE h2;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM h2;
Empty set (0.00 sec)
mysql> INSERT IGNORE INTO h2 VALUES (2, 5), (6, 10), (7, 5), (3, 1), (1, 9);
Query OK, 3 rows affected (0.00 sec)
Records: 5 Duplicates: 2 Warnings: 0
mysql> SELECT * FROM h2;
+------+------+
| c1 | c2 |
+------+------+
| 7 | 5 |
| 1 | 9 |
| 2 | 5 |
+------+------+
3 rows in set (0.00 sec)