我的 spamassassin 设置非常旧,已在两台机器之间迁移,并多次重新安装 mysql。我最近注意到,在运行sa-learn
消息时,我的日志中会出现大量错误,如下所示:
bayes: _put_token: SQL error: Incorrect string value: '\x8A\x98s\x9A\xC8' for column 'token' at row 1
我检查了数据库并做了一些研究,发现这个非常古老的错误报告确定了问题所在:我的架构(非常)过时了。列的排序规则bayes_token.token
是CHAR(5)
utf8_general_ci,而 9 年前它应该是BINARY(5)
。
我不能简单地转换列类型,因为许多 UTF8 5 字符值长于 5 个字节(该表有~110K 行)。
我的问题是:是否有任何方法可以截断过长的标记,以使其在贝叶斯分类器中保持有效?或者,如果做不到这一点,我可以只删除那些行,以便将剩余部分转换为二进制吗?
更新:我确实设法将列内容转移到BINARY(5)
表中的添加列(此处名为token2
),如下所示:
UPDATE bayes_token SET token2 = CONVERT(token USING latin1);
这是我所能想到的最接近“追溯”插入标记时所发生的情况的方法。但是,结果列中有许多重复项,并且由于该token
列是主键的一部分,因此这样做不好。
我认为可以保存数据,但可能不能用纯 SQL。我需要:
- 根据最新架构创建空表的新副本
- 将每个标记的所有重复项分组,并将这些行的
ham_count
值spam_count
和atime
最大值相加 - 将这些汇总结果插入到新表中
- 用新表替换原始表
答案1
听起来文本可能有不同的编码。除非您先转换为 utf8mb4,否则您不能将不同的编码放入单个列中。但是,如果您不知道编码是什么,那就不切实际了。
如果您的目标只是传递字节,我会使用VARBINARY(..)
或BLOB
,这样您就不会遇到字符集问题。
在 MySQL 中,foo VARCHAR(5) CHARACTER SET utf8
最多可占用 15 个字节。对于 utf8mb4,最多可占用 20 个字节。因此VARBINARY(20)
,没有CHARACTER SET
。
如果你的表foo
中当前有这样声明的内容,那么
ALTER TABLE t
MODIFY COLUMN foo BINARY(20);
(根据需要添加NULL
或NOT NULL
。)
答案2
我相信我已经尽我所能重建了数据,如下所示。
token2 BINARY(5) NOT NULL
向表中添加列bayes_token
UPDATE bayes_token SET token2 = CONVERT(token USING latin1);
bayes_token2
使用现代模式创建新表INSERT INTO bayes_token2 SELECT '1' AS id, token2 AS token, SUM(spam_count) AS spam_count, SUM(ham_count) AS ham_count, MAX(atime) AS atime FROM bayes_token GROUP BY token2;
- 删除表格
bayes_token
并替换为bayes_token2
无论如何,绝大多数数据都是唯一的,并且在 UTF8 中不超过 5 个字节,但我认为我这样做的方式也以正确的方式保存了剩余部分。