最近遇到一个达梦数据库报错“-6111: 字符串转换出错”的问题,这个问题主要是涉及到一条sql语句的执行,在此分享下这个报错的处理过程。

问题表现为:一样的表结构和数据,执行相同的SQL,在Oracle数据库中执行正常,到达梦数据库执行报错。

SQL语句大致如下:

SELECT "START_VAL", "END_VAL"
FROM 
(
    SELECT FLOOR(C2 / 200000) * 200000 AS START_VAL, CEIL(C2 / 200000) * 200000 - 1 AS END_VAL
    FROM T1
    WHERE C3 IN ('测试1', '测试2')
    GROUP BY FLOOR(C2 / 200000) * 200000, CEIL(C2 / 200000) * 200000 - 1
) 
WHERE START_VAL < END_VAL;

问题小记-达梦数据库报错“字符串转换出错”处理_数据库

还有一个现象, 如果把最后的条件“WHERE START_VAL < END_VAL”去掉,SQL就可以正常执行,结果如下图。

问题小记-达梦数据库报错“字符串转换出错”处理_数据库_02

客户尝试过对SQL语句中C2列使用to_number和CAST函数做显示类型转换,仍然会报错。这里不做赘述。

其实既然涉及到字符串转换出错,那问题的表象就很明显了,一定是查询语句中对应列字段真的涉及到字符串转换才会出错,数据库不会凭空报错的。

这里根据当时最终排查的结果,人工构造一批测试数据来复现下当时的情况,然后看下当时的分析过程:

--创建表
DROP TABLE IF EXISTS T1;
CREATE TABLE T1(C1 INT, C2 VARCHAR2(20), C3 VARCHAR2(50), C4 VARCHAR2(50),NOT CLUSTER PRIMARY KEY(C1));
--创建索引
CREATE INDEX IDX1 ON T1(C1 ASC,C3 ASC,C2 ASC);
--插入测试数据
DECLARE
BEGIN
    FOR I IN 1..1000 LOOP
        IF (I >= 1 AND I <= 200) THEN
            IF MOD(I, 3) = 0 THEN
                INSERT INTO T1 VALUES(I, DBMS_RANDOM.STRING('X', 10), DBMS_RANDOM.STRING('X', 10), DBMS_RANDOM.STRING('X', 10));
            ELSE
                INSERT INTO T1 VALUES(I, ABS(FLOOR(DBMS_RANDOM.VALUE(1000000000, 9999999999))), '测试1', DBMS_RANDOM.STRING('X', 10));
            END IF;
        ELSE
            IF I IN (119,120,911,10086,12345,12315,12580,96577) THEN
                INSERT INTO T1 VALUES(I, DBMS_RANDOM.STRING('X', 10), DBMS_RANDOM.STRING('X', 10), DBMS_RANDOM.STRING('X', 10));
            ELSE
                INSERT INTO T1 VALUES(I, ABS(FLOOR(DBMS_RANDOM.VALUE(1000000000, 9999999999))), '测试2', DBMS_RANDOM.STRING('X', 10));
            END IF;
        END IF;
    END LOOP;
    COMMIT;
END;
SELECT COUNT(*) FROM T1;  --共计1000条数据

注意:这里的表结构中只是本次模拟表结构随意创建的C1字段主键,实际业务场景中表字段很多,C1列也并不是主键,且表T1中包含C2和C3列的索引不止一条。表实际数据有几千万条,这里只做大致的问题模拟。

出现问题的SQL语句很简单,且只涉及到一张表,查询语句只涉及到T1表的两个字段列,分别为C2、C3,在SQL中能涉及到类型转换报错的,可以大胆判断是FLOOR和CEIL函数处理数据出现的问题。FLOOR和CEIL函数的功能如下: