Mysql中AND、OR的优先级导致的实际项目问题

项目中遇到问题:使用specification

理想的sql:where status=? and (enterprise_name=? or credit_code=? or (legal_person_name=? and legal_person_certificate_no=?))

specification拼接的sql:where status=? and (enterprise_name=? or credit_code=? or legal_person_name=? and legal_person_certificate_no=?)

List<Predicate> predicateAndList = new ArrayList<>();
            predicateAndList.add(criteriaBuilder.equal(root.get("status"), SwindleReportEnterpriseStatusEnum.CONFIRM));
            Predicate andStatus = criteriaBuilder.and(predicateAndList.toArray(new Predicate[predicateAndList.size()]));
            // 组装4个条件的or
            List<Predicate> predicateOrList = new ArrayList<>();
            String enterpriseName = enterpriseBase.getEnterpriseName();
            if (StringUtils.isNotBlank(enterpriseName)) {
                predicateOrList.add(criteriaBuilder.equal(root.get("enterpriseName"), enterpriseName));
            }
            String creditCode = enterpriseBase.getCreditCode();
            if (StringUtils.isNotBlank(creditCode)) {
                predicateOrList.add(criteriaBuilder.equal(root.get("creditCode"), creditCode));
            }
            // 法人信息的过滤
            List<Predicate> predicateLegalList = new ArrayList<>();
            String legalPersonName = enterpriseBase.getLegalPersonName();
            if (StringUtils.isNotBlank(legalPersonName)) {
                predicateLegalList.add(criteriaBuilder.equal(root.get("legalPersonName"), legalPersonName));
            }
            String legalPersonCertificateNo = enterpriseBase.getLegalPersonCertificateNo();
            if (StringUtils.isNotBlank(legalPersonCertificateNo)) {
                predicateLegalList.add(criteriaBuilder.equal(root.get("legalPersonCertificateNo"), legalPersonCertificateNo));
            }
            // 法人名、证件号
            Predicate andLegal = criteriaBuilder.and(predicateLegalList.toArray(new Predicate[predicateLegalList.size()]));
            predicateOrList.add(andLegal);
            // 企业名、统一信用代码
            Predicate orEnterInfo = criteriaBuilder.or(predicateOrList.toArray(new Predicate[predicateOrList.size()]));
            return criteriaQuery.where(andStatus, orEnterInfo).getRestriction();

从打印的sql看最后少了一个括号,这个问题折腾了许久未果,和同事讨论了一下,最终发现是对的,其实以上的两个sql是等价的。

因为sql里面的and的优先级高于or,所以打印的sql没有括号!!!

使用本地数据测试:

现需要查询出性别为女的1年级女学生,或者性别为女的2班级女学生。SQL语句如下:

  1. select * from student where sex='女' and grade=1 or class=2

  2. 但是该sql查询出来的结果并不符合要求,执行结果如下:

执行结果

执行的结果中还查询出了班级为2的男学生,显然结果是不正确的。

4、修改下SQL语句,添加上括号。如下:

  1. select * from student where sex='女' and (grade=1 or class=2)

该sql查询出来的结果符合要求,执行结果如下:

执行结果

分析

从上面的场景中,问题的关键就在于AND和OR的执行顺序问题。
查阅资料,关系型运算符优先级高到低为:NOT >AND >OR
如果where 后面有OR条件的话,则OR自动会把左右的查询条件分开。
就如上面场景中的第一条语句,他的查询条件分为两部分(或):

  1. 1、sex='女' and grade=1

  2. 2、 class=2

这也就是查询出1年级的女学生,2班级的学生。不符合最初查询的要求。
那么解决办法就是使用括号区分执行的顺序
就如上面场景的第二条语句,查询条件分为两部分(并):

  1. 1、 sex='女'

  2. 2、 (grade=1 or class=2)

Guess you like

Origin blog.csdn.net/qq_33371766/article/details/118339221