SQL 为值区间填充缺失值

SQL 为值区间填充缺失值,你想列出整个 20 世纪 80 年代里每年新入职的员工人数,但有一些年份并没有新增员工。你希望返回如下所示的结果集。

SQL 为值区间填充缺失值 问题描述

你想列出整个 20 世纪 80 年代里每年新入职的员工人数,但有一些年份并没有新增员工。你希望返回如下所示的结果集。

YR          CNT
---- ----------
1980          1
1981         10
1982          2
1983          1
1984          0
1985          0
1986          0
1987          0
1988          0
1989          0

SQL 为值区间填充缺失值 解决方案

本解决方案的关键之处在于如何为那些没有新增员工的年份返回 0。如果在一个给定的年份里没有新入职的员工,那么 EMP 表里就不存在对应的行。既然表里不包含这一年,我们该如何为这一年返回计数值 0 呢?本解决方案需要用到外连接操作。我们要拼凑一个包含了所有目标年份的结果集,然后针对 EMP 表执行 COUNT 查询,以判断每一年里是否新增了员工。
DB2
EMP 表作为数据透视表(因为它有 14 行数据),并调用内置函数 YEAR,为 20 世纪 80 年代的每一个年份生成一行数据。然后,外连接 EMP 表,并计算每年新增了多少名员工。

 1 select x.yr, coalesce(y.cnt,0) cnt
 2   from (
 3 select year(min(hiredate)over()) -
 4        mod(year(min(hiredate)over()),10) +
 5        row_number()over()-1 yr
 6   from emp fetch first 10 rows only
 7        )x
 8   left join
 9        (
10 select year(hiredate) yr1, count(*) cnt
11   from emp
12  group by year(hiredate)
13        ) y
14     on ( x.yr = y.yr1 )

Oracle
EMP 表作为数据透视表(因为它有 14 行数据),并调用内置函数 TO_NUMBERTO_CHAR,为 20 世纪 80 年代的每一个年份生成一行数据。然后,外连接 EMP 表,并计算每年新增了多少名员工。

 1  select x.yr, coalesce(cnt,0) cnt
 2    from (
 3  select extract(year from min(hiredate)over()) -
 4         mod(extract(year from min(hiredate)over()),10) +
 5         rownum-1 yr
 6   from emp
 7  where rownum <= 10
 8        ) x,
 9        (
10  select to_number(to_char(hiredate,'YYYY')) yr, count(*) cnt
11    from emp
12   group by to_number(to_char(hiredate,'YYYY'))
13         ) y
14   where x.yr = y.yr(+)

如果使用的是 Oracle 9i 及后续版本,则不妨使用新提供的 JOIN 子句。

 1  select x.yr, coalesce(cnt,0) cnt
 2    from (
 3  select extract(year from min(hiredate)over()) -
 4         mod(extract(year from min(hiredate)over()),10) +
 5         rownum-1 yr
 6    from emp
 7   where rownum <= 10
 8         ) x
 9    left join
10         (
11  select to_number(to_char(hiredate,'YYYY')) yr, count(*) cnt
12    from emp
13   group by to_number(to_char(hiredate,'YYYY'))
14         ) y
15      on ( x.yr = y.yr )

PostgreSQLMySQL
T10 表作为数据透视表(因为它有 10 行数据),并调用内置函数 EXTRACT,为 20 世纪 80 年代的每一个年份生成一行数据。然后,外连接 EMP 表,并计算每年新增了多少名员工。

 1  select y.yr, coalesce(x.cnt,0) as cn
 2    from (
 3  select min_year-mod(cast(min_year as int),10)+rn as yr
 4    from (
 5  select (select min(extract(year from hiredate))
 6            from emp) as min_year,
 7        id-1 as rn
 8    from t10
 9         ) a
10         ) y
11    left join
12         (
13  select extract(year from hiredate) as yr, count(*) as cnt
14    from emp
15   group by extract(year from hiredate)
16         ) x
17      on ( y.yr = x.yr )

SQL Server
EMP 表作为数据透视表(因为它有 14 行数据),并调用内置函数 YEAR,为 20 世纪 80 年代的每一个年份生成一行数据。然后,外连接 EMP 表,并计算每年新增了多少名员工。

 1  select x.yr, coalesce(y.cnt,0) cnt
 2    from (
 3  select top (10)
 4         (year(min(hiredate)over()) -
 5          year(min(hiredate)over( ))%10)+
 6          row_number()over(order by hiredate)-1 yr
 7    from emp
 8         ) x
 9    left join
10         (
11  select year(hiredate) yr, count(*) cnt
12    from emp
13   group by year(hiredate)
14         ) y
15      on ( x.yr = y.yr )

SQL 为值区间填充缺失值 扩展知识

尽管各个数据库的解决方案在语法上有所不同,做法却并无二致。内嵌视图 X 先找出最早的 HIREDATE 值对应的年份,进而返回 20 世纪 80 年代的每一年。下一步是用最早的年份减去该年份模 10 计算的结果,然后再加上 RN–1。为了更清楚地了解其工作原理,我们不妨实际运行一下内嵌视图 X,并分别返回其中涉及的每一个值。下面列出了两个版本的内嵌视图 X,它们分别使用窗口函数 MIN OVER(DB2、Oracle 和 SQL Server)和标量子查询(MySQLPostgreSQL)来得到对应的结果集。

select year(min(hiredate)over()) -
       mod(year(min(hiredate)over()),10) +
       row_number()over()-1 yr,
       year(min(hiredate)over()) min_year,
       mod(year(min(hiredate)over()),10) mod_yr,
       row_number()over()-1 rn
  from emp fetch first 10 rows only
 
  YR   MIN_YEAR     MOD_YR         RN
---- ---------- ---------- ----------
1980       1980          0          0
1981       1980          0          1
1982       1980          0          2
1983       1980          0          3
1984       1980          0          4
1985       1980          0          5
1986       1980          0          6
1987       1980          0          7
1988       1980          0          8
1989       1980          0          9
 
select min_year-mod(min_year,10)+rn as yr,
       min_year,
       mod(min_year,10) as mod_yr
       rn
  from (
select (select min(extract(year from hiredate))
          from emp) as min_year,
       id-1 as rn
  from t10
       ) x
 
  YR   MIN_YEAR     MOD_YR         RN
---- ---------- ---------- ----------
1980       1980          0          0
1981       1980          0          1
1982       1980          0          2
1983       1980          0          3
1984       1980          0          4
1985       1980          0          5
1986       1980          0          6
1987       1980          0          7
1988       1980          0          8
1989       1980          0          9

内嵌视图 Y 返回每一个 HIREDATE 对应的年份,以及那一年新增的员工人数。

select year(hiredate) yr, count(*) cnt
  from emp
 group by year(hiredate)
 
   YR        CNT
----- ----------
 1980          1
 1981         10
 1982          2
 1983          1

最后,外连接内嵌视图 Y 到内嵌视图 X,这样即使某一年没有新增员工,也会返回这一年的计数结果。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程