这是我的表:
sql
CREATE TABLE `collect_data_node` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`node_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '节点ip',
`measure_frequency` double DEFAULT NULL COMMENT '测量频率',
`measure_bandwidth` bigint(20) DEFAULT NULL COMMENT '测量带宽',
`measure_power` float DEFAULT NULL COMMENT '测量功率',
`monitor_model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '监测模式',
`collect_time` datetime DEFAULT NULL COMMENT '采集时间',
`channel_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4296575 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='信号检测节点采集数据表';
sql
CREATE DEFINER=`root`@`%` PROCEDURE `clean_up_old_data`(IN nodeIps TEXT, IN amount INT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cur_nodeIp VARCHAR(255);
DECLARE cur_count INT;
DECLARE cur CURSOR FOR
SELECT DISTINCT node_ip
FROM collect_data_node
WHERE FIND_IN_SET(node_ip, nodeIps);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO cur_nodeIp;
IF done THEN
LEAVE read_loop;
END IF;
-- 计算当前 nodeIp 的记录数
SELECT COUNT(*)
INTO cur_count
FROM collect_data_node
WHERE node_ip = cur_nodeIp;
-- 如果当前 nodeIp 的记录数超过指定数量,则删除多余记录
IF cur_count > amount THEN
DELETE FROM collect_data_node
WHERE node_ip = cur_nodeIp
and id NOT IN (
SELECT id FROM (
SELECT id
FROM collect_data_node
WHERE node_ip = cur_nodeIp
ORDER BY collect_time DESC
LIMIT amount
) AS temp
);
END IF;
END LOOP;
CLOSE cur;
END
sql
CALL clean_up_old_data2 (
'97bfc421-94ef-4a58-8be2-7234baf3954d',
1000
)
当我调用这个存储过程会报错:
1267 - Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation 'find_in_set'
find_in_set中涉及了排序
node_ip排序规则是:utf8mb4_0900_ai_ci,而输入变量nodeIps用的是默认的排序规则utf8mb4_unicode_ci。两个排序规则不一样,就报这个错了。
查看数据默认使用的排序规则:
sql
SHOW VARIABLES LIKE 'collation_server';

发现是utf8mb4_unicode_ci
解决方案
创建表的时候排序规则指定成数据库默认的排序规则。
修改存储过程,有涉及到排序的手动指定排序规则,保持一致的排序规则。
sql
CREATE DEFINER=`root`@`%` PROCEDURE `clean_up_old_data`(IN nodeIps TEXT, IN amount INT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cur_nodeIp VARCHAR(255);
DECLARE cur_count INT;
DECLARE cur CURSOR FOR
SELECT DISTINCT node_ip
FROM collect_data_node
WHERE FIND_IN_SET(
node_ip COLLATE utf8mb4_unicode_ci,
nodeIps COLLATE utf8mb4_unicode_ci
);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO cur_nodeIp;
IF done THEN
LEAVE read_loop;
END IF;
-- 计算当前 nodeIp 的记录数
SELECT COUNT(*)
INTO cur_count
FROM collect_data_node
WHERE node_ip COLLATE utf8mb4_0900_ai_ci = cur_nodeIp COLLATE utf8mb4_0900_ai_ci;
-- 如果当前 nodeIp 的记录数超过指定数量,则删除多余记录
IF cur_count > amount THEN
DELETE FROM collect_data_node
WHERE node_ip COLLATE utf8mb4_0900_ai_ci = cur_nodeIp COLLATE utf8mb4_0900_ai_ci
and id NOT IN (
SELECT id FROM (
SELECT id
FROM collect_data_node
WHERE node_ip COLLATE utf8mb4_0900_ai_ci = cur_nodeIp COLLATE utf8mb4_0900_ai_ci
ORDER BY collect_time DESC
LIMIT amount
) AS temp
);
END IF;
END LOOP;
CLOSE cur;
END