MySQL 5.7前不支持TEXT字段的COUNT(DISTINCT),5.7+可能静默截断前1024字节;推荐用哈希值(如SHA256)替代原始文本去重,或子查询+DISTINCT兜底,并统一处理NULL与空白值。

长文本字段用 COUNT(DISTINCT) 会报错或截断
MySQL 5.7 之前版本对 TEXT、MEDIUMTEXT 等长文本类型不支持 COUNT(DISTINCT),直接执行会报 ERROR 1140;5.7+ 虽允许,但内部可能静默截断前 1024 字节再比较,导致去重结果错误。PostgreSQL 和 SQL Server 对长文本支持较好,但性能会明显下降。
实操建议:
- 先确认字段实际长度分布:
SELECT MAX(LENGTH(content)), AVG(LENGTH(content)) FROM logs - 若多数值超过 500 字符,别硬套
COUNT(DISTINCT content) - MySQL 下可用
SUBSTRING(content, 1, 2000)显式截取后计数(前提是业务允许忽略超长部分差异)
用哈希值替代原始文本做去重统计
对长文本计算确定性哈希(如 SHA256),再对哈希值去重,既规避长度限制,又保持语义唯一性。注意:不同数据库哈希函数名不同,且需确保 NULL 处理一致。
常见写法:
- MySQL:
COUNT(DISTINCT SHA2(content, 256))(content为TEXT字段) - PostgreSQL:
COUNT(DISTINCT ENCODE(DIGEST(content, 'sha256'), 'hex')) - SQL Server:
COUNT(DISTINCT HASHBYTES('SHA2_256', ISNULL(content, '')))
⚠️ 注意:HASHBYTES 在 SQL Server 中对输入长度有限制(最大 8000 字节),超长需先 LEFT(content, 8000) 截断。
子查询 + DISTINCT 是最兼容的兜底方案
当哈希不可用(比如需要保留原始值用于后续关联)或数据库太老(如 SQLite 3.20 之前),必须退回到物化去重。虽然多一层嵌套,但语义清晰、各库都支持。
标准写法:
SELECT COUNT(*) FROM ( SELECT DISTINCT content FROM logs WHERE content IS NOT NULL ) AS deduped;
关键点:
- 显式加
WHERE content IS NOT NULL,否则某些数据库(如旧版 SQLite)会把所有 NULL 当作一行计入 - 如果字段含大量空格或换行,先
TRIM(content)再去重,避免“看似相同实则不同” - 大数据量时,该查询易触发磁盘临时表,建议在
WHERE中加时间范围过滤(如created_at > '2026-01-01')
NULL 和空白值的处理最容易被忽略
长文本字段常混杂 NULL、空字符串 ''、全空格字符串。而 COUNT(DISTINCT) 把 NULL 当作一个值(只计 1 次),却完全忽略空字符串——这两者在业务上往往应视为同一类“无效内容”,但数据库不会自动合并。
正确做法是统一归一化:
- 把 NULL 和空格都转成同一个占位符:
COUNT(DISTINCT COALESCE(NULLIF(TRIM(content), ''), 'NULL_VALUE')) - 千万别用
COUNT(DISTINCT COALESCE(content, ''))——这会让 NULL 和''成为两个不同值 - 如果业务要求完全排除无效内容,就别归一化,改用
WHERE TRIM(content) != ''过滤后再统计


















