先说背景,重构项目,发现有如下sql
select
id,
name,
company,
SUM(
if(
firstTimes > 0
and firstUploadYmd = #{ymd}
or secondTimes > 0
and secondUploadYmd = #{ymd}
or thirdTimes > 0
and thirdUploadYmd = #{ymd}
or forthTimes > 0
and forthUploadYmd = #{ymd},
1,0)
) as num
from d_scan;
咋一看这个sql怎么这个样子啊,写的太乱了吧!但是仔细揣摩一下,还是可以看出来这个sql要表达的语义。
查询符合以下条件的数据:
第1次次数大于0且第1次上传时间是ymd
或
第2次次数大于0且第2次上传时间是ymd
或
第3次次数大于0且第3次上传时间是ymd
或
第4次次数大于0且第4次上传时间是ymd
但是这个sql语句这样写真的没问题吗?难道不应该这样写吗?
select
id,
name,
company,
SUM(
if(
(firstTimes > 0 and firstUploadYmd = #{ymd})
or
(secondTimes > 0 and secondUploadYmd = #{ymd})
or
(thirdTimes > 0 and thirdUploadYmd = #{ymd})
or
(forthTimes > 0 and forthUploadYmd = #{ymd}),
1,0)
) as num
from d_scan;
最后将sql改成上面这样,发现2个sql语句的执行结果是一样的。
带着这样的疑问,执行了以下sql语句,顿时恍然大悟。
SELECT 1>2 //返回0
SELECT 1>2 AND 1>2 //返回0
SELECT 1>2 AND 1>2 OR 2>1 //返回1
此时的结果符合预期。
SELECT 1>2 AND 1>2 OR 2>1 AND 1>2 //返回0
等等,这里为什么返回的是0?
1>2 AND 1>2 返回的是0, 1>2 AND 1>2 OR 2>1返回的是1, 1>2 AND 1>2 OR 2>1 AND 1>2返回的是0
那就是说最后1个 AND 1>2影响了最终的结果。
相当于整个sql语句每执行一次判断,返回的结果0或者1继续和下面的判断做与或操作。
0 and 0 返回0
0 and 1 返回0
1 and 0 返回0
1 and 1 返回1
0 or 0 返回0
0 or 1 返回1
1 or 0 返回1
1 or 1 返回1
个人是这么理解的:返回0的部分可以直接丢弃,返回1的部分保留。
最后执行的就是OR后面的部分 SELECT 2>1 AND 1>2
那么是否可以理解为OR后面的语句都自带括号,直到遇到下一个OR
SELECT 1>2 AND 1>2 OR 2>1 AND 1>2 等价于 SELECT 1>2 AND 1>2 OR (2>1 AND 1>2)
如果这么解释的话,上面的2个sql语句为什么执行结果一样,也就解释的通了。
虽然明白了其中的原理,但是从代码可读性以及可维护性的角度出发,最好不要这样写sql,能加括号的还是加上吧,避免出现歧义误导人。
SELECT 2>1 AND 2>1 OR 1>2 and 1>2 OR 2>3 and 2>3
这个sql语句等价于
SELECT 2>1 AND 2>1 OR (1>2 and 1>2) OR (2>3 and 2>3)
2>1 AND 2>1 返回1,后续的OR不需要执行直接返回1即可。
最后验证下学习成果,下面的sql返回0还是1?
SELECT 1>2 AND 1>2 OR 2>1 AND 1>2 OR 2>1 AND 2>1 AND 2>1 AND 2>3
等价于
SELECT 1>2 AND 1>2 OR (2>1 AND 1>2) OR (2>1 AND 2>1 AND 2>1 AND 2>3)
最后返回0。