【SQL开发实战技巧】系列(七):从有重复数据前提下如何比较出两个表中的差异数据及对应条数聊起

简介: 本篇文章讲解的主要内容是:***如果有重复数据如何检查出两个表中的差异数据及对应条数、表连接做聚合容易出现重复计算的错误、多表查询空值处理问题、NOT IN的子查询范围不能是空值,否则查询结果为空。***

前言

本篇文章讲解的主要内容是:如果有重复数据如何检查出两个表中的差异数据及对应条数、表连接做聚合容易出现重复计算的错误、多表查询空值处理问题、NOT IN的子查询范围不能是空值,否则查询结果为空。
【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。


一、如果有重复数据如何检查出两个表中的差异数据及对应条数

有这么一个临时表

with t as (
SELECT* FROM emp WHERE deptno != 10
UNION ALL
SELECT* FROM emp WHERE ename = 'SCOTT'
)
select * from t

要求用查询找出T与表emp中不同的数据。
注意:T中员工SCOTT有两行数据,而emp表中只有一条数据。

SQL> with t as (
  2  SELECT* FROM emp WHERE deptno != 10
  3  UNION ALL
  4  SELECT* FROM emp WHERE ename = 'SCOTT'
  5  )
  6  select rownum,empno,ename from t where ename='SCOTT';

    ROWNUM EMPNO ENAME
---------- ----- ----------
         1  7788 SCOTT
         2  7788 SCOTT

SQL> select rownum,empno,ename from emp where ename='SCOTT';

    ROWNUM EMPNO ENAME
---------- ----- ----------
         1  7788 SCOTT

比较两个数据集的不同时,通常用类似下面的FULL JOIN语句:

with t as
 (SELECT *
    FROM emp
   WHERE deptno != 10
  UNION ALL
  SELECT *
    FROM emp
   WHERE ename = 'SCOTT')
select t.empno, t.ename, e.empno, e.ename
  from t
  full join emp e
    on (t.empno = e.empno)
 where t.empno is null
    or e.empno is null;

EMPNO ENAME      EMPNO ENAME
----- ---------- ----- ----------
                  7782 CLARK
                  7839 KING
                  7934 MILLER
                  1001 test

但是这种语句在这个案例中查不到SCOTT的区别。那我们应该怎么才能查到呢?
我们可以先对数据进行处理,增加一列显示相同数据的条数,再进行比较:

with t as
 (SELECT *
    FROM emp
   WHERE deptno != 10
  UNION ALL
  SELECT *
    FROM emp
   WHERE ename = 'SCOTT')
select t.empno, t.ename, e.empno, e.ename
  from (select empno, ename, count(*) cnt from t group by empno, ename) t
  full join (select empno, ename, count(*) cnt
               from emp
              group by empno, ename) e
    on (t.empno = e.empno and t.cnt = e.cnt)
 where t.empno is null
    or e.empno is null;

EMPNO ENAME      EMPNO ENAME
----- ---------- ----- ----------
                  1001 test
                  7782 CLARK
                  7788 SCOTT
                  7839 KING
                  7934 MILLER
 7788 SCOTT            

6 rows selected

二、表连接做聚合容易出现重复计算的错误

现在有一张员工级别临时表:

with t as (
select '7934' as empno,1 as lev from dual 
union all
select '7934' as empno,2 as lev from dual 
union all
select '7839' as empno,3 as lev from dual 
union all
select '7782' as empno,1 as lev from dual 
)

员工的奖金根据lev计算,lev=1的奖金为员工工资的10%,lev=2的奖金为员工工资的20%,lev=3的奖金为员工工资的30%。
现要求返回上述员工(也就是部门10的所有员工)的工资及奖金。
如果你马上想到的是先关联,然后对结果做聚集。那么你可以尝试一下,会发现7934'的工资被重复计算了两次,正确的做法应该是先对T表做聚合然后再关联!正确的SQL放到下面!

with t as (
select '7934' as empno,1 as lev from dual 
union all
select '7934' as empno,2 as lev from dual 
union all
select '7839' as empno,3 as lev from dual 
union all
select '7782' as empno,1 as lev from dual 
),
t1 as (
select empno,
       sum(case when lev = 1 then 0.1 when lev = 2 then 0.2 when lev = 3 then
           0.3end) as levs
  from t
 group by empno
)
select deptno, sum(sal) as sumsal, sum(sal * t1.levs) as sumlevs
  from emp
 inner join t1
    on (emp.empno = t1.empno)
 where deptno = 10
 group by deptno

三、多表查询空值处理问题

先看下当前emp表数据:

SQL> select * from emp
  2  ;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
 7369 SMITH      CLERK      7902 1980-12-17     800.00               20
 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30
 7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30
 7566 JONES      MANAGER    7839 1981-4-2      2975.00               20
 7654 MARTIN     SALESMAN   7698 1981-9-28     1250.00   1400.00     30
 7698 BLAKE      MANAGER    7839 1981-5-1      2850.00               30
 7782 CLARK      MANAGER    7839 1981-6-9      2450.00               10
 7788 SCOTT      ANALYST    7566 1987-4-19     3000.00               20
 7839 KING       PRESIDENT       1981-11-17    5000.00               10
 7844 TURNER     SALESMAN   7698 1981-9-8      1500.00      0.00     30
 7876 ADAMS      CLERK      7788 1987-5-23     1100.00               20
 7900 JAMES      CLERK      7698 1981-12-3      950.00               30
 7902 FORD       ANALYST    7566 1981-12-3     3000.00               20
 7934 MILLER     CLERK      7782 1982-1-23     1300.00               10
 1001 test                       2021-10-9 1                     

15 rows selected

接下来我有一个需求:让我们查找comm小于1400的员工信息:

SQL> select * from emp where comm < 1400;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30
 7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30
 7844 TURNER     SALESMAN   7698 1981-9-8      1500.00      0.00     30

不对呀,哪些comm是null的员工也得查出来啊,那么我们在查询的时候要注意,如果你不能保证你要比较大小的列(比如comm),那你写sql的时候要注意,对空值一定要做个转换,正确的写法:

SQL> select * from emp where coalesce(comm,0) < 1400;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
 7369 SMITH      CLERK      7902 1980-12-17     800.00               20
 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30
 7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30
 7566 JONES      MANAGER    7839 1981-4-2      2975.00               20
 7698 BLAKE      MANAGER    7839 1981-5-1      2850.00               30
 7782 CLARK      MANAGER    7839 1981-6-9      2450.00               10
 7788 SCOTT      ANALYST    7566 1987-4-19     3000.00               20
 7839 KING       PRESIDENT       1981-11-17    5000.00               10
 7844 TURNER     SALESMAN   7698 1981-9-8      1500.00      0.00     30
 7876 ADAMS      CLERK      7788 1987-5-23     1100.00               20
 7900 JAMES      CLERK      7698 1981-12-3      950.00               30
 7902 FORD       ANALYST    7566 1981-12-3     3000.00               20
 7934 MILLER     CLERK      7782 1982-1-23     1300.00               10
 1001 test                       2021-10-9 1                     

14 rows selected

上面我还是使用的coalesce,你也可以用nvl,前面文章讨论过这俩的区别了。

四、NOT IN的子查询范围不能是空值,否则查询结果为空

先看一个查询:

SQL> SELECT* FROM dept WHERE deptno NOT IN (SELECT emp.deptno FROM emp WHERE emp.deptno  IS NOT NULL);

DEPTNO DNAME          LOC
------ -------------- -------------
    40 OPERATIONS     BOSTON

如果我们把这个WHERE emp.deptno IS NOT NULL去掉呢?

SQL> SELECT* FROM dept WHERE deptno NOT IN (SELECT emp.deptno FROM emp);

DEPTNO DNAME          LOC
------ -------------- -------------

SQL> 

发现没有查询结果了,这是因为啥?
我们之前文章介绍过,NULL不支持加、减、乘、除、大小比较、相等比较,否则只能为空。
所以这里类比成SELECT* FROM dept WHERE deptno NOT IN (null),那结果肯定是空值了,这里一定要记住,如果你不能保证你的not in子查询范围一定不为空,那一定要加上null值过滤条件,否则你的查询结果是错误的!


总结

本章介绍的如果有重复数据如何检查出两个表中的差异数据及对应条数、表连接做聚合容易出现重复计算的错误、多表查询空值处理问题、NOT IN的子查询范围不能是空值,否则查询结果为空这四个案例,是工作中非常容易遇到的场景,也是很容易犯错的地方,博主写出来也给自己一个提醒!

相关文章
|
17天前
|
SQL 存储 缓存
SQL Server 数据太多如何优化
11种优化方案供你参考,优化 SQL Server 数据库性能得从多个方面着手,包括硬件配置、数据库结构、查询优化、索引管理、分区分表、并行处理等。通过合理的索引、查询优化、数据分区等技术,可以在数据量增大时保持较好的性能。同时,定期进行数据库维护和清理,保证数据库高效运行。
|
1月前
|
SQL 移动开发 Oracle
SQL语句实现查询连续六天数据的方法与技巧
在数据库查询中,有时需要筛选出符合特定时间连续性条件的数据记录
|
1月前
|
SQL 存储 关系型数据库
添加数据到数据库的SQL语句详解与实践技巧
在数据库管理中,添加数据是一个基本操作,它涉及到向表中插入新的记录
|
1月前
|
SQL 数据挖掘 数据库
SQL查询每秒的数据:技巧、方法与性能优化
id="">SQL查询功能详解 SQL(Structured Query Language,结构化查询语言)是一种专门用于与数据库进行沟通和操作的语言
|
1月前
|
SQL 监控 数据处理
SQL数据库数据修改操作详解
数据库是现代信息系统的重要组成部分,其中SQL(StructuredQueryLanguage)是管理和处理数据库的重要工具之一。在日常的业务运营过程中,数据的准确性和及时性对企业来说至关重要,这就需要掌握如何在数据库中正确地进行数据修改操作。本文将详细介绍在SQL数据库中如何修改数据,帮助读者更好
210 4
|
4月前
|
SQL JSON 数据库
influxdb 端点使用http进行sql查询,写数据
influxdb 端点使用http进行sql查询,写数据
272 0
|
1月前
|
SQL
使用SQL进行集合查询和数据维护
使用SQL进行集合查询和数据维护
37 0
|
3月前
|
SQL 存储 分布式计算
|
3月前
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之sql查询如何导出全量数据
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
3月前
|
SQL 移动开发 Oracle
SQL查询连续六天数据记录的技巧与方法
在数据库查询中,实现针对连续几天(如连续六天)的数据筛选是一个常见且稍具挑战性的任务