我正在使用MySQL 5.7。我创建了一个带有DATETIME类型的虚拟列(未存储)的表,其中包含索引。当我正在研究它时,我注意到order by并没有返回所有数据(我在顶部期待的一些数据丢失了)。 MAX和MIN的结果也是错误的。 我跑完后
ANALYZE TABLE
CHECK TABLE
OPTIMIZE TABLE
然后结果是正确的。我想索引数据存在问题,所以我几乎没有问题:
我担心将来会发生这种情况,但我不会注意到。
修改:
根据评论中的要求添加了表格定义:
CREATE TABLE `items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned DEFAULT NULL,
`image` json DEFAULT NULL,
`status` json DEFAULT NULL,
`status_expired` tinyint(1) GENERATED ALWAYS AS (ifnull(json_contains(`status`,'true','$.expired'),false)) VIRTUAL COMMENT 'used for index: it checks if status contains expired=true',
`lifetime` tinyint(4) NOT NULL,
`expiration` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
`last_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `expiration` (`status_expired`,`expiration`) USING BTREE,
CONSTRAINT `ts_competition_item_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `ts_user_core` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1312459 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
返回错误结果的查询:
SELECT * FROM items ORDER BY expiration DESC;
SELECT max(expiration),min(expiration) FROM items;
由于
答案 0 :(得分:9)
问题是您的数据来自通过索引实现的虚拟列。检查,优化,分析您正在执行的操作会强制同步索引并修复任何错误。从而为您提供正确的结果。至少在索引再次失去同步之前。
许多问题都是由您的表格设计问题引起的。我们先来看看。
`status_expired` tinyint(1) GENERATED ALWAYS AS (ifnull(json_contains(`status`,'true','$.expired'),false)) VIRTUAL
毫无疑问,这是为了克服这样一个事实:无法直接索引mysql中的JSON
列。您已创建了一个虚拟列,并将其编入索引。这一切都很好,但是这个列只能包含两个值中的一个; true
或false
。这意味着它的基数很差。因此,mysql不太可能将此索引用于任何事情。
但我们可以看到,在创建索引时,您已将status_expired
列与expired
列合并。也许是为了克服上面提到的这种可怜的基数。但是等等......
`expiration` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
到期是另一个虚拟列。这会产生一些影响。
在生成的虚拟列上创建辅助索引时, 生成的列值在索引的记录中具体化。 如果索引是覆盖索引(包含所有列的索引) 通过查询检索),从中检索生成的列值 索引结构中的具体化值而不是“在...上计算” 飞”。
这与
相反VIRTUAL:不存储列值,但在行数时会对其进行求值 在任何BEFORE触发器之后立即读取。虚拟列不需要 存储
参考:https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
我们根据声音主体创建虚拟列,不应存储由列上的简单操作生成的值以避免冗余,但通过在其上创建索引,我们会重新引入冗余。
根据提供的信息,您似乎不需要status_expired
列甚至expired
列。超过它的失效日期的项目已过期!
CREATE TABLE `items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned DEFAULT NULL,
`image` json DEFAULT NULL,
`status` json DEFAULT NULL,
`expire_date` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
`last_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `expiration` (`expired_date`) USING BTREE,
CONSTRAINT `ts_competition_item_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `ts_user_core` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1312459 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
当您需要找出哪些项目已过期时,只需将当前日期与上表中的expired_date列进行比较。这里的区别在于,expired
不是每个查询中的计算项,而是在创建记录时计算expiry_date
一次。
这使你的桌子更整洁,查询可能更快