mysql 错误代码:1503:唯一索引必须包括表中的所有列,当存在唯一索引时,如何对表进行分区?

dxpyg8gm  于 12个月前  发布在  Mysql
关注(0)|答案(2)|浏览(125)


的数据
列定义
ID列是具有自动增量的主键,我们使用它来维护池范围。
串行是唯一的,即使在分区后也需要是唯一的。
产品的状态是值为A或U的枚举。
问题:
我想根据状态对表进行分区。大多数SQL查询都基于状态为A的主键或序列号。当我尝试使用下面的查询创建分区时,它不起作用。

ALTER TABLE product
PARTITION BY LIST COLUMNS(status)
(
   PARTITION used VALUES IN ('U'),
   PARTITION used VALUES IN ('A')
);
Error Code : 1503: A Unique Index must include all columns in the table

字符串
谁能告诉我解决这个问题的正确方法?

9w11ddsr

9w11ddsr1#

您不能使用ENUM -分区表达式中不允许使用此数据类型。请使用CHAR(1)和MySQL 8.0.16或更高版本。如果您的版本低于此版本,请在触发器中检查此条件。
不能将serial定义为具有相应索引的UNIQUE,因为任何唯一索引都必须包括分区表达式中使用的所有列。请改用触发器。
主键必须是复合的-它必须包含status列(见上文)和id列作为前缀(自动递增所需)。如果您需要单独检查id唯一性,请相应地扩展触发器代码。

示例

创建表格:

CREATE TABLE product (
    id BIGINT UNSIGNED AUTO_INCREMENT,
    `serial` VARCHAR(16) NOT NULL,
    status CHAR(1) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, status),
    CHECK (status IN ('A', 'U'))
);

字符串
创建触发器:

CREATE TRIGGER tr1
BEFORE INSERT ON product
FOR EACH ROW 
BEGIN
    IF EXISTS ( SELECT NULL 
                FROM product 
                WHERE `serial` = NEW.`serial`
                ) THEN 
        SIGNAL SQLSTATE '45000' 
            SET MESSAGE_TEXT = 'The column ''serial'' must be unique.';
    END IF;
END
CREATE TRIGGER tr2
BEFORE UPDATE ON product
FOR EACH ROW 
BEGIN
    IF EXISTS ( SELECT NULL
                FROM product
                WHERE `serial` = NEW.`serial`
                  AND OLD.`serial` <> NEW.`serial`
                ) THEN 
        SIGNAL SQLSTATE '45000' 
            SET MESSAGE_TEXT = 'The column ''serial'' must be unique.';
    END IF;
END

的数据
修改表,创建分区:

ALTER TABLE product
PARTITION BY LIST COLUMNS(status)
(
   PARTITION used VALUES IN ('U'),
   PARTITION not_used VALUES IN ('A')
);


插入法律的行:

INSERT INTO product (`serial`, status) VALUES ('ABC', 'A');
INSERT INTO product VALUES (DEFAULT, 'DEF', 'U', DEFAULT);


通过serial插入重复-错误:

INSERT INTO product (`serial`, status) VALUES ('DEF', 'A');
The column 'serial' must be unique.

的字符串
插入无效的status值-错误:

INSERT INTO product (`serial`, status) VALUES ('GHI', 'X');
Check constraint 'product_chk_1' is violated.

serial更新为现有值-错误:

UPDATE product SET `serial` = 'ABC' WHERE `serial` = 'DEF';
The column 'serial' must be unique.

更新为serial未更改-触发器允许:

UPDATE product SET created_at = NOW() + INTERVAL 1 DAY WHERE status = 'A';


参见表格内容:

SELECT * FROM product;


| ID|串行|地位|创建于|
| --|--|--|--|
| 2 |DEF| U| 2023-12-21 06:07:14|
| 1 |ABC|一|2023-12-22 06:07:14|
请参阅单分区内容:

SELECT * FROM product PARTITION (used);


| ID|串行|地位|创建于|
| --|--|--|--|
| 2 |DEF| U| 2023-12-21 06:07:14|
fiddle

zd287kbt

zd287kbt2#

如果serial是唯一的,并且不太可能更改,那么就摆脱id,并使用

PRIMARY KEY(serial)

字符串
(请解释“维护池范围”;也许id有一些用处。
“希望根据状态对表进行分区”。相反,请考虑使用复合索引,如

INDEX(status, serial)
INDEX(status, created_at)


有了这些,WHERE status = 'U' ORDER BY created_at DESC将在没有分区的情况下运行得一样快或更快。
正如评论所提到的,这在某些情况下可能是有用的:

INDEX(serial, status)


“selecting 15000 rows”--客户端用这么多行做什么?我希望它阻塞。
你有日期,id是什么?
当遍历一个大表时,“记住你离开的地方”。即使PK是复合的,这也可以做到。当我看到你需要的各种查询时,让我们进一步讨论。(它可能涉及一个复合PRIMARY KEY,它是为那些大扫描的性能而设计的。)
更多关于“离开”在我的博客下午Pagination

相关问题