我的 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-char 值长于 5 个字节(表有 ~110K 行)。
我的问题是:是否有任何方法可以截断过长的标记,使它们在贝叶斯分类器中保持有效?或者如果失败了,我可以只删除那些行,以便我可以将其余部分转换为二进制?
更新:我确实设法将列内容传输到BINARY(5)
表中的一个添加列(这里命名为token2
),如下所示:
UPDATE bayes_token SET token2 = CONVERT(token USING latin1);
这与我所能想到的“退回”插入时令牌发生的事情一样接近。但是,结果列中有许多重复项,并且由于该token
列是主键的一部分,这不好。
我认为数据可以保存,但可能不是纯 SQL。我需要:
- 从最新模式创建空表的新副本
- 对每个标记的所有重复项进行分组,并将这些行的值
ham_count
和spam_count
最大值相加atime
- 将这些聚合结果插入到新表中
- 用新表替换原来的表
听起来文本可能是不同的编码。除非您首先转换为 utf8mb4,否则您不能将不同的编码放入单个列中。但是,如果你不知道编码是什么,那是不切实际的。
如果您的目标只是传递字节,我会使用
VARBINARY(..)
orBLOB
这样您就不会因字符集问题而绊倒。在 MySQL 中,
foo VARCHAR(5) CHARACTER SET utf8
最多可以占用 15 个字节。对于 utf8mb4,最多 20 个字节。所以VARBINARY(20)
,没有CHARACTER SET
。如果您的表当前包含
foo
在其中,以这种方式声明,请执行(附加
NULL
或NOT NULL
酌情。)我相信我已经尽我所能重构了数据,如下所示。
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 个字节,但我认为我这样做的方式也以正确的方式保存了其余部分。