SQL 删除重复数据

SQL 删除重复数据,你正在生成一个报表,当相邻两行的某列出现了相同值时,你希望那个值只显示一次。例如,你想从 EMP 表中提取出 DEPTNOENAME,希望按照 DEPTNO 对所有的行进行分组,并且希望每个 DEPTNO 只显示一次。你希望返回如下所示的结果集。

SQL 删除重复数据 问题描述

你正在生成一个报表,当相邻两行的某列出现了相同值时,你希望那个值只显示一次。例如,你想从 EMP 表中提取出 DEPTNOENAME,希望按照 DEPTNO 对所有的行进行分组,并且希望每个 DEPTNO 只显示一次。你希望返回如下所示的结果集。

DEPTNO ENAME
------ ---------
    10 CLARK
       KING
       MILLER
    20 SMITH
       ADAMS
       FORD
       SCOTT
       JONES
    30 ALLEN
       BLAKE
       MARTIN
       JAMES
       TURNER
       WARD

SQL 删除重复数据 解决方案

这是一个很简单的格式化问题,Oracle 提供的窗口函数 LAG OVER 能很容易地解决这一问题。当然,使用标量子查询和其他窗口函数也能达到同样目的(对于非 Oracle 用户只能如此),但这里最方便的做法是使用 LAG OVER 函数。
DB2 和 SQL Server
使用窗口函数 MIN OVER 为每个 DEPTNO 找出最小的 EMPNO,然后使用 CASE 表达式来“涂改”那些 EMPNO 不等于该最小值的行。

 1  select case when empno=min_empno
 2              then deptno else null
 3         end deptno,
 4         ename
 5    from (
 6  select deptno,
 7         min(empno)over(partition by deptno) min_empno,
 8         empno,
 9         ename
10    from emp
11         ) x

Oracle
使用窗口函数 LAG OVER 访问当前行的前一行,为每个分区间找出第一个 DEPTNO

1 select to_number(
2           decode(lag(deptno)over(order by deptno),
3                 deptno,null,deptno)
4        ) deptno, ename
5   from emp

PostgreSQLMySQL
本实例着重介绍如何使用窗口函数方便地访问到当前行前面和后面的行数据。在写作本书时,这些数据库尚未支持这类窗口函数。

SQL 删除重复数据 扩展知识

DB2 和 SQL Server
首先,使用窗口函数 MIN OVER 找出每个 DEPTNO 对应的最小 EMPNO 值。

select deptno,
       min(empno)over(partition by deptno) min_empno,
       empno,
       ename
  from emp
 
DEPTNO MIN_EMPNO       EMPNO ENAME
------ ---------- ---------- ----------
    10       7782       7782 CLARK
    10       7782       7839 KING
    10       7782       7934 MILLER
    20       7369       7369 SMITH
    20       7369       7876 ADAMS
    20       7369       7902 FORD
    20       7369       7788 SCOTT
    20       7369       7566 JONES
    30       7499       7499 ALLEN
    30       7499       7698 BLAKE
    30       7499       7654 MARTIN
    30       7499       7900 JAMES
    30       7499       7844 TURNER
    30       7499       7521 WARD

下一步也是最后一步,使用 CASE 表达式删除重复的 DEPTNO。如果一个员工的 EMPNOMIN_EMPNO 相等,则返回 DEPTNO,否则返回 Null

select case when empno=min_empno
            then deptno else null
       end deptno,
       ename
  from (
select deptno,
       min(empno)over(partition by deptno) min_empno,
       empno,
       ename
  from emp
       ) x
 
DEPTNO ENAME
------ ----------
    10 CLARK
       KING
       MILLER
    20 SMITH
       ADAMS
       FORD
       SCOTT
       JONES
    30 ALLEN
       BLAKE
       MARTIN
       JAMES
       TURNER
       WARD

Oracle
首先,使用窗口函数 LAG OVER 为每一行返回前一行的 DEPTNO

select lag(deptno)over(order by deptno) lag_deptno,
       deptno,
       ename
  from emp
 
LAG_DEPTNO     DEPTNO ENAME
---------- ---------- ----------
                   10 CLARK
        10         10 KING
        10         10 MILLER
        10         20 SMITH
        20         20 ADAMS
        20         20 FORD
        20         20 SCOTT
        20         20 JONES
        20         30 ALLEN
        30         30 BLAKE
        30         30 MARTIN
        30         30 JAMES
        30         30 TURNER
        30         30 WARD

如果仔细观察以上结果集的话,很容易区分出哪些行的 DEPTNOLAG_DEPTNO 相等。对于这些行,我们希望把 DEPTNO 显示为 Null。我们可以借助 DECODE 函数做到这一点(TO_NUMBER 函数可以把 DEPTNO 转换为数字)。

select to_number(
           decode(lag(deptno)over(order by deptno),
                  deptno,null,deptno)
       ) deptno, ename
  from emp
 
DEPTNO ENAME
------ ----------
    10 CLARK
       KING
       MILLER
    20 SMITH
       ADAMS
       FORD
       SCOTT
       JONES
    30 ALLEN
       BLAKE
       MARTIN
       JAMES
       TURNER
       WARD

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程