记一次多次智商下线导致的奇耻大辱

记一次多次智商下线导致的奇耻大辱

出来混,智商是关键,智商下线总会把一件简单的事情复杂化,导致花费更多的时间却做不好事情。最近刚好遇到这样的一次经历,记录下来,用来提醒自己。

第一次问题反馈

公司技服反馈,公司产品在某处使用过程中发生查询结果错的问题,可能涉及到保密问题,所以说的比较笼统,原文如下:

写了一过程,声明了一带参数的游标,限定了查询结果,可是运行发现这个游标查询的是一个全集。

第一次智商下线

简单的分析了一下反馈内容,脑海中简单回忆了一下,没有关于带参游标的概念,然后去查阅了一下公司产品的帮助文档,也没有发现关于带参游标的概念,于是想当然的认为对方的意思表达的有问题,可能是想说存储过程带参数,然后游标里面用到了这个参数作为限定条件,但是执行之后发现限定条件没起作用,导致结果多了。
没有用例,于是打算自己编写用例复现问题,然后分析原因。
用例如下:

create table test(col int);
insert into test values(1);
insert into test values(2);
insert into test values(3);

create or replace procedure sel(con in int) as
declare
    cursor csr for select * from test where con = col;//查询表test的col列与过程sel的参数con一致的结果
    ret sel % rowtype;
begin
    open csr;
    fetch csr into ret;
    while csr % found loop
        dbms_output.put_line(ret.col);
        fetch csr into ret;
    end loop;
    close csr;
end;

select * from test; //预期结果3条1 2 3

exec sel(1); //预期结果只有一条 1

exec sel(2); //预期结果只有一条 2

exec sel(3); //预期结果只有一条 3

简单执行后,发现结果符合预期,所以算是复现失败,于是找到技服,进一步了解。

第二次智商下线

简单问过技服之后,技服说有过电话沟通,那边说是字符串类型。于是,第二次智商下线,简单的修改用例,改成如下用例:

create table test(col varchar(10));
insert into test values('1');
insert into test values('2');
insert into test values('3');

create or replace procedure sel(con in varchar(10)) as
declare
    cursor csr for select * from test where con = col;//查询表test的col列与过程sel的参数con一致的结果
    ret sel % rowtype;
begin
    open csr;
    fetch csr into ret;
    while csr % found loop
        dbms_output.put_line(ret.col);
        fetch csr into ret;
    end loop;
    close csr;
end;

select * from test; //预期结果3条'1' '2' '3'

exec sel(1); //预期结果只有一条 '1'

exec sel(2); //预期结果只有一条 '2'

exec sel(3); //预期结果只有一条 '3'

用例执行,显然,结果依然符合预期,复现再次失败,仔细反思流程,终于觉得是不是忽略了用户所说的带参游标的问题,于是终于想起了度娘。百度发现带参游标是真实存在的,于是找到了一个简单的用例试验。
用例如下:

create or replace procedure a
as
  cursor b(c_id int) is select * from d where id=c_id;
  begin
    open b(111);
  end;

在自己那里试验了一下之后,发现语法总是通不过,于是再次想当然的认为用户所说的带参游标公司产品确实不支持,所以按照用户的说法第二套复现应该能够成功,但是仍然失败,怀疑是公司产品的一些周边产品的问题。想请技服再次与用户沟通,进一步的确定问题所在。

第三次智商下线

与技服沟通过程中,技服是个负责任的人,于是说,确定不支持带参游标么?于是百度了一下,把上面的用例执行在自己本地安装的公司产品上试验了一下,发现能够通过。好吧,这时才发现虽然公司帮助文档中没有提供说明,但确确实实支持带参游标这个功能,而我本地语法通不过是因为使用的是产品的某个分支版本,该版本不支持。
于是借用技服的机器构造用例:

create table test(col varchar(10));
insert into test values('1');
insert into test values('2');
insert into test values('3');

create or replace procedure sel(con in varchar(10)) as
declare
    csr(csr_con varchar(10)) cursor is select * from test where csr_con = col;//查询表test的col列与游标csr的参数csr_con一致的结果,csr_con的值由con提供
    ret sel % rowtype;
begin
    open csr(con);
    fetch csr into ret;
    while csr % found loop
        dbms_output.put_line(ret.col);
        fetch csr into ret;
    end loop;
    close csr;
end;

exec sel('1'); //预期一条结果 '1'
exec sel('2'); //预期一条结果 '2'
exec sel('3'); //预期一条结果 '3'

执行之后,发现结果依然符合预期,说明复现依然失败,感觉凭目前的条件无法判断问题所在,于是请技服与用户再次沟通。

第二次问题反馈

依然是由于保密,因此这次沟通只得到了两张照片和一段话,由于没有找到合适图床,这里图片不贴出来。图片的内容中主要是过程的名和参数,以及游标的全部定义。

扫描二维码关注公众号,回复: 1047254 查看本文章

这个T_CLASSID = ‘AA’根据这三个参数进行查询的T_PNAME等参数结果并不相符,这个T_PNAME反查回去的classid不是AA。

第四次智商下线

由于用例和数据依然不完整,但有了大概的轮廓,因此依据这些进行了复现用例的书写:

create table rt_ttapall(rsltype varchar(10), snum number, pname varchar(10), palia varchar(10), 
    missid number, objid number, classid varchar(10),
    subsys number);

insert into rt_tta_pall values('KA0', 0, 'KeyNo0', 'GNCC当',
    11, 1, 'AA',
    10);
insert into rt_tta_pall values('KA0', 0, 'KeyNo0', 'GNCC当',
    11, 1, 'AA',
    10);
insert into rt_tta_pall values('KA0', 0, 'KeyNo0', 'GNCC当',
    12, 1, 'AA',
    10);
insert into rt_tta_pall values('KA0', 0, 'KeyNo0', 'GNCC当',
    11, 2, 'AA',
    10);
insert into rt_tta_pall values('KA0', 0, 'KeyNo0', 'GNCC当',
    11, 1, 'BB',
    10);

create or replace procedure pro_in_telpara(t_telmissid in numeric(10, 0), t_telobjid in numeric(10, 0), t_classid in varchar(10)) as
declare
    t_rslttype varchar(10);
    t_snum number;
    t_pname varchar(10);
    t_palia varchar(10);

    cursor cur_telpara(missno number, objno number, classid varchar(10)) is
        select rsltype, snum, pname, palia
        from rt_ttapall
        where MISSID = missno and CLASSID = classid and OBJID = objno
        order by subsys, snum;
begin
    open cur_telpara(t_telmissid, t_telobjid, t_classid);
    fetch cur_telpara into t_rslttype, t_snum, t_pname, t_palia;

    while cur_telpara % found loop //输出所有符合条件的结果
        dbms_output.put_line(t_rslttype + ' ' + t_snum + ' ' + t_pname + ' ' + t_palia);
        fetch cur_telpara into t_rslttype, t_snum, t_pname, t_palia;
    end loop;
    dbms_output.put_line(cur_telpara % rowcount); //输出结果个数 
    close cur_telpara;
end;

--执行过程
exec pro_in_telpara(11, 1, 'AA');

--直接查询
select rsltype, snum, pname, palia
from rt_ttapall
where MISSID = 11 and OBJID = 1 and CLASSID = 'AA' 
order by subsys, snum;

如上的用例,执行后期望两边执行结果一致,均为两条结果,即按照 11 1 ‘AA’三个条件筛选后的两条数据,但执行后发现select语句直接执行的结果正确,但存储过程执行后的结果确实多了一条,看起来非常像’AA’条件过滤失效或者条件丢失。于是再次想当然的认为成功复现了用户说明的问题而且想当然的认为上面的两个用例的结果应该一致,通知技服给用户反馈,说已经成功复现,确实是我们这边的问题,马上着手修改。

第五次智商下线

问题成功复现,于是开始着手修改,首先分析是游标处理过程中将’AA’条件丢失,于是想要确认最终的执行计划上是否有该条件。但发现过程执行时后台不会输出对应的执行计划,没办法,想到先查看select的查询计划,期望游标方式也会生成对应的计划,看计划的差异。
select的计划很简单,这边执行的过程中是将三个值当作Const直接下放到底层的scan key,按照三个条件确定该算子的返回数据条数,然后上层sort算子执行order by过程。代码调试过程中,发现也确实是这样,三个条件均为Expr表达式,然后昨天是Var,右边是Const,认证了刚才的推想,那么也就是说,返回结果数是有scan key决定的,只要检查游标方式生成的scan key的条件哪里出了问题就可以了,在生成scan key的位置打了断点,进行调试。
调试的过程中,发现游标方式也会生成同样的scan key,但是上面的条件却不一致,11和1的条件与select的计划类似,一边是Var,另一边是Param。但是刚好’AA’条件不一致,是一个NullTest,也就是说只判断是否为空。于是再次想当然的认为之前的设想得到了验证,确实是计划生成过程中将’AA’条件丢失,而变成了NullTest,于是一步步倒推,希望找到问题发生的位置。
1 计划的生成位置,已经是NullTest了。
2 Query生成的为知,Expr已经变成空。
3 …
正在继续调试,以为马上就要找到问题原因的时候,技服接到用户电话,说在他执行的用例中把游标的参数改成classno,问题就没有了,没有了,没有了。
松了一口气的同时,却觉得怎么可能!改个名问题就解决了,和名字有什么关系!
于是回到刚才的用例,分析了一下游标部分,发现按照用户的操作之后,where条件MISSID = missno and CLASSID = classid and OBJID = objno会变成MISSID = missno and CLASSID = classno and OBJID = objno,区别只是classid变成classno,执行了一下,发现结果确实正确了。
那么,问题到底在哪呢?难道执行结果还和字段名有关?不可能,那还怎么用!
仔细分析一下,才回想起来我们的产品并不区分大小写,也就是说,MISSID = missno and CLASSID = classid and OBJID = objno实际上是MISSID = MISSNO AND CLASSID = CLASSID AND OBJID = OBJNO,中间的条件左右两边是一样的!那么是不是因为这个,导致条件变成了表中的字段CLASSID = 表中的字段CLASSID,横真,相当于没有条件。。。
于是果断将游标参数objno也改成了objid,发现该条件也会不起作用,而若游标参数与表中字段不一致时,执行结果均正确,好吧,问题确实是这个。
也就是说,这么漫长的用例复现和调试都是在做无用功,无用功,无用功!

总结

  1. 在无法获得具体用例时,自己书写用例或者复现的过程中,务必保证你的环境与用户的一致,或者差异绝对不会影响复现!避免多走弯路。
  2. 尽量与提出问题的人多做沟通,如果实在沟通不方便的话,那么一切以问题提出人的说法为标准。当然,问题提出者也有可能出错,除了客观实际的错误外,一切相信提出者的描述可以让你尽可能的理解别人的意思,而不会曲解别人的意思,导致走弯路。
  3. 在得到用户的用例时,一定要仔细分析用例想要的结果,并结合自己公司的产品确定用例是否会得到想要的结果。

后记

文中说明的问题为工作中遇到的某次问题的实际记录,这里记录下来是为了总结原因,给自己一个警钟。

猜你喜欢

转载自blog.csdn.net/weixin_42182073/article/details/80371306