ORACLE学习之ORACLE常见错误分析和例子

说明

本文主要介绍常见的理解或误解sql语法错误

使用count的理解,实际上是分组函数

错误理解

COUNT(某字段),COUNT(自然数或字符串)和COUNT(*)的查询某表的数据量结果完全一致

正确的理解

  • COUNT(某字段)指的是此字段非空情况下的数据量的统计
  • COUNT(自然数或字符串)指定此列值全部是此自然数或字符串,并方便准确计数,不存在有NULL的值,常习惯使用COUNT(1){推荐使用}
  • COUNT(*)指的是此表所有字段的非空情况下的数据量的统计

代码举例

现已知hyz_emp有9999条数据
(1)使用COUNT(某字段)或COUNT(1)
SELECT COUNT(s.comm) FROM hyz_emp s;--查询结果是:9975,实际情况数据量应当为9999
SELECT * FROM hyz_emp s;--查询结果存在9999条数据
SELECT COUNT(1) FROM hyz_emp s;--查询结果存在9999条数据
SELECT COUNT(s.comm) FROM hyz_emp s WHERE s.comm IS NULL;--查询结果为0:,实际情况应为24
SELECT COUNT(1) FROM hyz_emp s WHERE s.comm IS NULL;--查询结果存在24条数据
SELECT * FROM hyz_emp s WHERE s.comm IS NULL;--查询结果存在24条数据
(2)使用COUNT(*)
SELECT COUNT(*) FROM hyz_emp s;--查询结果存在9999条数据,
--一般情况下,表中会定义主键几乎不会存在所有字段为空的情况发生,即使这样,建议使用COUNT(1)对某表进行计数,效率上会高

结论:

  • (1)使用COUNT(某字段)时,如果此字段为NULL,会忽略并且不纳入计数,为了保障计数正确用使用count(1),效率也高
  • (2)使用COUNT(*)一般情况下都能准确计算某表的真实数据量,但是效率上较低,遇到上千万数据会明显卡顿
  • (3)事实上分组函数皆不会取NULL值

使用=和!=的理解(一)

错误理解

!=条件就是说某字段与某字符串或数字匹配完全不一致的所有数据

正确理解

  • =条件就是说某字段与某字符串或数字匹配完全一致的所有数据
  • !=条件就是说某字段与某字符串或数字匹配完全不一致,且IS NOT NULL的所有数据

代码举例

SELECT *
  FROM hyz_emp s
 WHERE s.deptno = 10 --deptno=10,查询结果值满足deptno的值是10的数据
   AND s.mgr = 1 --对应的领导empno是1的数据,查询结果值满足mgr的值是1的数据
   AND s.hiredate BETWEEN DATE '2017-4-1' AND DATE '2017-5-1'; --一共38条数据,comm字段为null的有2条
SELECT *
  FROM hyz_emp s
 WHERE s.deptno = 10 --deptno=10,查询结果值满足deptno的值是10的数据
   AND s.mgr = 1 --对应的领导empno是1的数据,查询结果值满足mgr的值是1的数据
   AND s.hiredate BETWEEN DATE '2017-4-1' AND DATE '2017-5-1'
   AND s.comm = 1; --一共1条数据,如果按错误理解,当应comm!=1的时候会有37条数据才对
SELECT *
  FROM hyz_emp s
 WHERE s.deptno = 10 --deptno=10,查询结果值满足deptno的值是10的数据
   AND s.mgr = 1 --对应的领导empno是1的数据,查询结果值满足mgr的值是1的数据
   AND s.hiredate BETWEEN DATE '2017-4-1' AND DATE '2017-5-1'
   AND s.comm != 1; --实际上与预想恰好相反,查询结果只有35条,那么那两条去哪了?
SELECT *
  FROM hyz_emp s
 WHERE s.deptno = 10 --deptno=10,查询结果值满足deptno的值是10的数据
   AND s.mgr = 1 --对应的领导empno是1的数据,查询结果值满足mgr的值是1的数据
   AND s.hiredate BETWEEN DATE '2017-4-1' AND DATE '2017-5-1'
   AND (CASE
         WHEN s.comm = 1 THEN
          0
         WHEN s.comm IS NULL THEN
          0
         ELSE
          1
       END) = 1; --其实!=条件和本句sql意思完全一致,通过本句可以看出!=是即满足IS NOT NULL 条件又要满足comm = 1之外的数据
SELECT *
  FROM hyz_emp s
 WHERE s.deptno = 10 --deptno=10,查询结果值满足deptno的值是10的数据
   AND s.mgr = 1 --对应的领导empno是1的数据,查询结果值满足mgr的值是1的数据
   AND s.hiredate BETWEEN DATE '2017-4-1' AND DATE
 '2017-5-1'
   AND (s.comm != 1 OR s.comm IS NULL); --此时正好得到我们想要的38条中comm!=1的时候应当有37的条数据的结果

结论:

!=条件指的是与此字段的值完全不一致,且满足IS NOT NULL的条件,同理NOT LIKE也是这样

使用=和!=条件的理解(二)

错误理解

=和!=在任何条件下都要保证两边的类型完全一致

正确理解

  • ORACLE数据库也会类似JAVA存在自动转型,一种很特殊的情况,即字符串的值为纯数字字符,此时即使使用字符串作为条件还是数字作为条件都能得到正确的结果,只是使用时一定要注意字符串的每个字符都会占位,条件的正确性直接影响结果,而数字0如果放置小数点及其整数前面不会影响结果
  • 日期类型和其他类型几乎都可以使用转换类型为其转为字符串类型,少部分可以转换为数字类型,视情况而定,只要保证=或!=左右两边类型完全一致就行

代码举例

--当empno_number定义为字符类型时且值为数字字符
--o.empno_number=数字类型条件时,无论是0634还是634都可以得到正确结果
--o.empno_number=数字字符的字符类型条件时,0634可以得到正确结果,但是634得不到正确结果,返回没有数据
--0634
WITH emp AS
 (SELECT to_char('0' || s.empno) AS empno_number, s.*
    FROM hyz_emp s
   WHERE s.empno = 634)
SELECT * FROM emp o where o.empno_number=0634;
--634
WITH emp AS
 (SELECT to_char('0' || s.empno) AS empno_number, s.*
    FROM hyz_emp s
   WHERE s.empno = 634)
SELECT * FROM emp o where o.empno_number=634;
--0634
WITH emp AS
 (SELECT to_char('0' || s.empno) AS empno_number, s.*
    FROM hyz_emp s
   WHERE s.empno = 634)
SELECT * FROM emp o where o.empno_number='0634';
--634
WITH emp AS
 (SELECT to_char('0' || s.empno) AS empno_number, s.*
    FROM hyz_emp s
   WHERE s.empno = 634)
SELECT * FROM emp o where o.empno_number='634';

结论:

特殊情况下,如果字符串为纯粹的数字字符,此时ORACLE会在=和!=条件进行自动类型转换,这样也可正常使用=和!=条件,条件一定要合理和注意字符串的占位

使用外连接的理解

常用的是JOIN,LEFT JOIN,RIGHT JOIN

错误理解

LEFT JOIN,RIGHT JOIN查询结果完全一致,JOIN和LEFT JOIN,RIGHT JOIN查询结果不一致

正确理解

  • LEFT JOIN,RIGHT JOIN同条件情况下查询结果完全一致
  • JOIN和LEFT JOIN,RIGHT JOIN,如果条件不一致,可以达到查询结果完全一致
LEFT JOIN,RIGHT JOIN代码举例
--条件必须完全一致,结果才一致
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       o.loc
  FROM hyz_emp s
  LEFT JOIN hyz_dept o
    ON s.deptno = o.deptno
   AND s.job = o.dname;
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       o.loc
  FROM hyz_emp s
  right JOIN hyz_dept o
    ON s.deptno = o.deptno
   AND s.job = o.dname;
JOIN和LEFT JOIN,RIGHT JOIN代码举例
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       m.grade,
       m.losal,
       m.hisal
  FROM hyz_emp s
  JOIN hyz_salgrade m
    ON s.sal BETWEEN m.losal AND m.hisal
   AND m.grade = 2 --只取工资等级在2的数据
 ORDER BY s.hiredate, s.ename;
--等价于
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       m.grade,
       m.losal,
       m.hisal
  FROM hyz_emp s
  LEFT JOIN hyz_salgrade m
    ON s.sal BETWEEN m.losal AND m.hisal
 WHERE m.grade = 2 --只取工资等级在2的数据
 ORDER BY s.hiredate, s.ename;
--如果使用不当会得到意想不到结果,当然这种情况在业务处理时为了保证数据量可能根据需求而选择,具体是否可取,还看业务需要
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       m.grade,
       m.losal,
       m.hisal
  FROM hyz_emp s
  LEFT JOIN hyz_salgrade m
    ON s.sal BETWEEN m.losal AND m.hisal
      --并没有只取工资等级在2的数据,而是所有的hyz_emp的9999条数据,如果和hyz_salgrade关联存在等于2的才有值
   AND m.grade = 2
 ORDER BY s.hiredate, s.ename;

rownum的使用理解

错误理解

可以直接使用>,>=,<=,<=,<>等不等式条件

正确理解

对于rownum伪列来说,是从1开始计数,因此如果使用大于符号,是找不到数据的,rownum的初始化就是1,始终无法跳过1去查询数据,但是可以把此字段当成一个普通的字段处理再次查询,就可以使用大于符号了,因此如果要使用大于号,只能从1开始,使其正常初始化

代码举例
--得不到正确的结果
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       rownum AS r
  FROM hyz_emp s
 WHERE rownum > 5;
--得到正确的前五个结果,截取成功
SELECT s.empno,
       s.ename,
       s.job,
       s.mgr,
       s.hiredate,
       s.sal,
       s.comm,
       s.deptno,
       rownum AS r
  FROM hyz_emp s
 WHERE rownum BETWEEN 1 AND 5;
--可以把伪列当成一个字段值去正常使用sql
SELECT l.empno,
       l.ename,
       l.job,
       l.mgr,
       l.hiredate,
       l.sal,
       l.comm,
       l.deptno,
       l.r --此时的r并不是伪列,而是实实在在的数据,可以正常使用sql,当然也可以不必显示此字段
  FROM (SELECT s.empno,
               s.ename,
               s.job,
               s.mgr,
               s.hiredate,
               s.sal,
               s.comm,
               s.deptno,
               rownum AS r --此时r是伪列,不能正常初始化时会得不到正确的结果
          FROM hyz_emp s
         WHERE rownum <= 500) l
--通过l.r的结果可知,确实是从300到500截取了一段数据,这常用于分页
 WHERE l.r >= 300;
--事实上,以上的从300-500截取的分页查询是效率最高的,也是最常用的

本人是一枚程序猿,如果觉得整理的不错,请关注个人微信公众号(扫一扫):

猜你喜欢

转载自blog.csdn.net/huyingzuo/article/details/80117059