一条长sql的排错过程
过程
有这样一条长sql,由于环境原因,对select.....in...... 语法限制使用,因此以left join语法代替,原来只需要统计一天的数据汇总结果,因为特殊需要,需要一次性统计各天的结果,于是首先将子查询中时间过滤的部分提取到外层。
修改前sql1:
select count(*) from (SELECT DISTINCT t.con_no AS con_no
, CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
FROM my_customer t
LEFT OUTER JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.stat = 1
AND tgua.type = 'C' and
(tgua.atime>= '2017-06-22' and tgua.atime< '2017-06-23')
LEFT OUTER JOIN my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL and
(tcc.ctime>= '2017-06-22' and tcc.ctime< '2017-06-23')
LEFT OUTER JOIN (
SELECT tcp.con_no AS con_no, tcai.CTIME AS CTIME
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO and
(tcai.ctime>= '2017-06-22' and tcai.ctime< '2017-06-23')
) bb
ON t.con_no = bb.con_no
WHERE t.PRO_METHOD = 'uc' and (tgua.con_no IS NOT NULL
OR tcc.con_no IS NOT NULL
OR bb.con_no IS NOT NULL)) pp order by pp.con_no
sql2:
select count(*) from
(SELECT DISTINCT t.con_no AS con_no
, CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
FROM my_customer t
LEFT OUTER JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.status = 1
AND tgua.type = 'C'
LEFT OUTER JOIN my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL
LEFT OUTER JOIN (
SELECT tcp.con_no AS con_no, tcai.CTIME AS CTIME
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO
) bb
ON t.con_no = bb.con_no
WHERE t.pro_method = 'uc' and (tgua.con_no IS NOT NULL
OR tcc.con_no IS NOT NULL
OR bb.con_no IS NOT NULL)) p where CTIME>= '2017-06-22' and CTIME< '2017-06-23'
order by con_no
试着执行了两条sql,发现sql2查询结果数量总比sql1要少,于是用*代替count(*),打印出具体查询结果,
sql3:
select * from (SELECT DISTINCT t.con_no AS con_no
, CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
FROM my_customer t
LEFT OUTER JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.stat = 1
AND tgua.type = 'C' and
(tgua.atime>= '2017-06-22' and tgua.atime< '2017-06-23')
LEFT OUTER JOIN my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL and
(tcc.ctime>= '2017-06-22' and tcc.ctime< '2017-06-23')
LEFT OUTER JOIN (
SELECT tcp.con_no AS con_no, tcai.CTIME AS CTIME
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO and
(tcai.ctime>= '2017-06-22' and tcai.ctime< '2017-06-23')
) bb
ON t.con_no = bb.con_no
WHERE t.PRO_METHOD = 'uc' and (tgua.con_no IS NOT NULL
OR tcc.con_no IS NOT NULL
OR bb.con_no IS NOT NULL)) pp order by pp.con_no
sql4:
select * from
(SELECT DISTINCT t.con_no AS con_no
, CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
FROM my_customer t
LEFT OUTER JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.status = 1
AND tgua.type = 'C'
LEFT OUTER JOIN my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL
LEFT OUTER JOIN (
SELECT tcp.con_no AS con_no, tcai.CTIME AS CTIME
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO
) bb
ON t.con_no = bb.con_no
WHERE t.pro_method = 'uc' and (tgua.con_no IS NOT NULL
OR tcc.con_no IS NOT NULL
OR bb.con_no IS NOT NULL)) p where CTIME>= '2017-06-22' and CTIME< '2017-06-23'
order by con_no
找出sql3比sql4多出的执行结果记录,如编号为4534的一条记录, 这条记录在sql4中很有可能被过滤掉了,因此修改sql4外层的where查询条件,如下:
select * from
(SELECT DISTINCT t.con_no AS con_no
, CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
FROM my_customer t
LEFT OUTER JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.status = 1
AND tgua.type = 'C'
LEFT OUTER JOIN my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL
LEFT OUTER JOIN (
SELECT tcp.con_no AS con_no, tcai.CTIME AS CTIME
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO
) bb
ON t.con_no = bb.con_no
WHERE t.pro_method = 'uc' and (tgua.con_no IS NOT NULL
OR tcc.con_no IS NOT NULL
OR bb.con_no IS NOT NULL)) p where con_no='4534'
查询结果发现CTIME对应的值竟是“2017-06-23 00:18:07“。而在sql3执行结果中,CTIME值为2017-06-22 00:16:08。问题出在这一sql片段中:
CASE
WHEN tgua.atime IS NOT NULL THEN tgua.atime
WHEN tcc.CTIME IS NOT NULL THEN tcc.CTIME
WHEN bb.CTIME IS NOT NULL THEN bb.CTIME
END AS CTIME, t.PRO_METHOD AS pro_method
在sql3的执行结果中,由于时间筛选在内层的left join后面,时间不是2017-06-22的记录字段值都用null代替,因此在结果集中,无论是tgua.atime,tcc.CTIME 还是bb.CTIME,时间不是null就是2017-06-22 ,而在sql4中,tgua.atime、tcc.CTIME 、bb.CTIME的时间是不一致的,因此假如case when选择一个not null的时间值,且该时间刚好不是2017-06-22,那么这条记录会在外层的where筛选中被过滤掉。而我们希望的却是tgua.atime、tcc.CTIME 、bb.CTIME只要有一个时间是在2017-06-22,那这条记录就应该出现在结果集中。使用union语法,修改如下:
select distinct * from
((SELECT DISTINCT t.con_no AS con_no
, DATE_FORMAT(tgua.atime,"%Y-%m-%d") AS ctime, t.PRO_METHOD AS pro_method
FROM my_customer t
inner JOIN first_ auth tgua
ON t.con_no = tgua.con_no
AND tgua.status = 1
AND tgua.type = 'C')
union
(SELECT DISTINCT t.con_no AS con_no
, DATE_FORMAT(tcc.ctime,"%Y-%m-%d") AS ctime, t.PRO_METHOD AS pro_method
FROM my_customer t
inner JOIN
my_ contact tcc
ON t.con_no = tcc.con_no
AND tcc.NAME IS NOT NULL)
union
(SELECT DISTINCT t.con_no AS con_no
,DATE_FORMAT(bb.ctime,"%Y-%m-%d") AS ctime, t.PRO_METHOD AS pro_method
FROM my_customer t
inner JOIN (
SELECT tcp.con_no AS con_no, tcai.ctime AS ctime
FROM my_ person tcp
INNER JOIN my_auth_info tcai
ON tcp.CNO = tcai.CNO
) bb
ON t.con_no = bb.con_no)) p where pro_method = 'uc' and ctime>= '2017-06-22' and ctime< '2017-06-23'
order by con_no
查出的记录数恰好与sql1中的一致,在此基础上进行group by分组统计处理。
总结
多表关联查询排错遇到问题时,要通过select *打印出具体的明细,进行对比,分析其特征,再反观sql思考其错误的可能原因,再修改,再排查,这是一个不断从现象到本质,从思考到实践的循环上升的过程,这也是修改一切bug的要旨所在。另外,越复杂的sql越容易出错,而且不利于修改维护,因此,在应用层编写代码时,sql越简单越好。
参考文章:mysql left( right ) join使用on 与where 筛选的差异
- 算法模板——左偏树(可并堆)
- 算法模板——二分图匹配
- Codevs2776 寻找代表元
- C#线程篇---解答线程之惑(2)
- webpack学习(三)html-webpack-plugin插件
- Codevs2018 反病毒软件
- 2953: [Poi2002]商务旅行
- 1230: [Usaco2008 Nov]lites 开关灯
- 【推荐】C#线程篇---你所不知道的线程池(4)
- 洛谷P1333 瑞瑞的木棍(欧拉回路)
- 【推荐】C#线程篇---Task(任务)和线程池不得不说的秘密(5.1)
- 【LeetCode 20】关关的刷题日记45 – Valid Parenthese
- 1819: [JSOI]Word Query电子字典
- 【推荐】C#线程篇---Task(任务)和线程池不得不说的秘密(5.2)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Saltstack_使用指南06_远程执行-指定目标
- 什么是缓存击穿、雪崩、穿透
- 如何卸载CDH7.1.1
- java8 stream的这些开发技巧,你值得好好收藏
- 面试官:mybatis中#{ }和${ }的区别
- python 学习笔记(9)——Python 正则表达式
- 万能的BeanPostProcessor是如何让spring无限扩展的?
- spring解决循环依赖为什么要用三级缓存?
- 深入剖析ThreadLocal
- spring事务的这10种坑,你稍不注意可能就会踩中!!!
- 面试前看了这篇spring事务的文章,让我多要了2k的工资
- 面试时被问到单例模式,怎么回答才能让面试官眼前一亮?
- 老司机手把手教你编写自己的springboot starter
- 实战|如何消除又臭又长的if...else判断更优雅的编程?
- 硬核 | 使用spring cache让我的接口性能瞬间提升了100倍