【问题标题】:SQL: Removing Duplicates rows while retaining the row with highest value in another columnSQL:删除重复行,同时在另一列中保留具有最高值的行
【发布时间】:2017-06-09 18:28:39
【问题描述】:

假设我有一个带有数据的表测试:

SOID SO_Name   SO_Desc     PRIORITY  ADE_PRIORITIZED  DEPLOY_DATE  ENV
123  SO1      SO1 Desc1      111      Y               01-JAN-01     0
123  SO1      SO1 Desc1      111      Y               01-JAN-01     1
123  SO1      SO1 Desc1      111      Y               01-JAN-01     2
123  SO1      SO1 Desc1      111      Y               01-JAN-01     3
987  SO1      SO1 Desc1      111      Y               01-JAN-01     0
987  SO1      SO1 Desc1      111      Y               01-JAN-16     1
987  SO1      SO1 Desc1      111      Y               21-JAN-17     2
987  SO1      SO1 Desc1      111      Y               01-JAN-17     3
121  SO121    SO121 Desc121  111      Y               01-JAN-17     0

我想删除每个 soid 的重复行(重复可以基于 4 列:so_name、so_desc、priority、ade_prioritized)保留具有最高 deploy_date 的行。

我使用了这个查询,但它没有删除任何行。

delete from so_test a 
where a.deploy_date < (
  select max(b.deploy_date) from so_test b where a.soid = b.soid
);

0 rows deleted

我期望的最终结果应该是: SOID SO_Name SO_Desc PRIORITY ADE_PRIORITIZED DEPLOY_DATE ENV 123 SO1 SO1 描述 111 是 01-JAN-01 0 987 SO1 SO1 描述 111 是 21-JAN-17 2 987 SO1 SO1 Desc1 111 Y 21-JAN-17 2

可能是什么问题? 没有CTE可以吗?

【问题讨论】:

  • 鉴于该数据,将删除零行,因为零行符合条件。对于 SOID=123,所有部署日期都是相同的......所以没有一个小于最大值。对于 SOID=121,只有一行,所以没有一个小于最大值。
  • 您可以使用 CTE(公用表表达式)和 Row_Number() 来做您想做的事……但是那边的“ENV”列是什么?你为什么不直接删除 ENV > 0 的地方?
  • 你用 mysql、sql server 和 oracle 标记了这个。是哪个?
  • @pmbAustin - 我已经编辑了它。现在可以检查了吗?

标签: mysql sql-server oracle duplicates sql-delete


【解决方案1】:

使用with (common table expression)row_number(),您可以识别并轻松处理重复项:

使用 ctes 时,您只能在表达式之后执行一条语句(除非您链接 ctes 或使用多个 ctes)。

在以下代码示例中,您将首先使用 select 检查输出,然后如果需要进一步操作,请注释掉 select 查询并取消注释 delete 查询。

rextester 链接:http://rextester.com/UFQQ51693

with cte as (
  select   
      *
    , rn = row_number() over (
            partition by soid 
            order by deploy_date desc
            )
    from [so_test]
)
/* --------------------------------------------------------------
-- This returns all of rows with values that have duplicates
-- along the row number (rn) so you can see which rows 
-- would be affected by the following actions
-------------------------------------------------------------- */
/*
select o.*
  from cte as o
  where exists (
      select 1
        from cte as i
        where cte.soid  = i.soid 
          and i.rn>1
      );
--*/
/* --------------------------------------------------------------
-- Remove duplicates by deleting all of the duplicates
-- where the row number (rn) is greater than 1
-- without deleting the first row of the duplicates.
-------------------------------------------------------------- */
--/*
delete 
  from cte 
  where cte.rn > 1 
--*/

删除后的测试结果:

+------+---------+---------------+----------+-----------------+---------------------+-----+
| soid | so_name |    so_desc    | priority | ade_prioritized |     deploy_date     | env |
+------+---------+---------------+----------+-----------------+---------------------+-----+
|  123 | SO1     | SO1_Desc1     |      111 | Y               | 01.01.2001 00:00:00 |   0 |
|  987 | SO1     | SO1_Desc1     |      111 | Y               | 21.01.2017 00:00:00 |   2 |
|  121 | SO121   | SO121_Desc121 |      111 | Y               | 01.01.2017 00:00:00 |   0 |
+------+---------+---------------+----------+-----------------+---------------------+-----+

【讨论】:

  • 如果您有大量重复项,使用非重复行创建新表可能比删除重复行更快或更有效。使用@sqlzim 显示的 CTE 和 row_number() 函数,您可以执行 CREATE TABLE as SELECT... (CTAS) 或 INSERT as SELECT。然后,您可以使用并行性和直接路径加载来获得最佳性能。
  • 我从未使用过 CTE,所以不确定如何使用。有没有其他办法晒黑CTE。我知道这里所有的部署日期对于 SOID 都是相同的,所以它不会被删除。但可能存在所有日期相同的情况,然后需要保留 1 行,如果 deply_date 不同,则应保留最大 nddeploy 日期,如果只保留 1 行。
  • @Mishti 我认为您应该学习如何使用 CTE。我添加了一个 rextester 链接,您可以尝试一下。
  • @sqlZim- 我在运行 CTE 语句时收到此错误:ORA-00923: FROM 关键字未在预期的位置找到 00923. 00000 - “FROM 关键字未在预期的位置找到” *原因:*操作:行错误:82 列:5 我正在使用 sqldeveloper64
【解决方案2】:

基于将非重复项保存到新表中的示例。

create table so_test_nodups 
as
with dups as 
( select soid, so_name, so_desc, priority, ade_prioritized, deploy_date, env,  
        row_number() over ( partition by so_name, so_desc, priority, ade_prioritized order by deploy_date desc ) rn 
  from so_test 
) 
select  soid, so_name, so_desc, priority, ade_prioritized, deploy_date, env 
from dups 
where rn=1 

查询 so_test_nodups 表。

select * from so_test_nodups

      SOID SO_NAME    SO_DESC                PRIORITY A DEPLOY_DA        ENV
---------- ---------- -------------------- ---------- - --------- ----------
       123 SO1        SO1 Desc1                   111 Y 01-JAN-01          0
       121 SO121      SO121 Desc121               111 Y 01-JAN-17          0

在提供的编辑后添加结果:

      SOID SO_NAME    SO_DESC                PRIORITY A DEPLOY_DA        ENV
---------- ---------- -------------------- ---------- - --------- ----------
       987 SO1        SO1 Desc1                   111 Y 21-JAN-17          2
       121 SO121      SO121 Desc121               111 Y 01-JAN-17          0

【讨论】:

  • 谢谢鲍勃。我已经编辑了,你现在可以检查吗?我还需要从同一个表中删除并只使用该表。
  • 创建 so_test_nodups 后,只需删除 so_test 并将 so_test_nodups 重命名为 so_test。
  • 我用编辑过的数据运行它(可以看到上面,它有 987 作为 soid),但它没有排在最高的 deploy_date 行。
  • @Mishti 我根据您的最新测试数据添加了一组新结果。见上文。
猜你喜欢
  • 2016-07-26
  • 2018-12-29
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 2021-07-13
  • 2012-09-11
  • 1970-01-01
  • 2014-03-29
相关资源
最近更新 更多