【问题标题】:Delete duplicate rows based on columns根据列删除重复行
【发布时间】:2017-08-08 03:14:51
【问题描述】:

我有一个名为Aircraft 的表,其中有很多记录。问题是有些是重复的。我知道如何选择重复项及其计数:

SELECT flight_id, latitude, longitude, altitude, call_sign, measurement_time, COUNT(*)
FROM Aircraft
GROUP BY flight_id, latitude, longitude, altitude, call_sign, measurement_time
HAVING COUNT(*) > 1;

这会返回类似:

现在,我需要做的是删除重复项,每个只留下一个,这样当我再次运行查询时,所有计数都变为 1。

我知道我可以使用 DELETE 关键字,但我不确定如何从 SELECT 中删除它。

我确定我错过了一个简单的步骤,但我不想破坏我的数据库作为一个新手。

我该怎么做?

【问题讨论】:

  • 没有。没有重复;所有行都不同。 什么您的预期结果是什么?
  • @wildplasser 嗯?
  • 哦,等等,结果中有 count(*) 列。表是否有 PK(如代理键)?

标签: postgresql


【解决方案1】:
SELECT
    flight_id, latitude, longitude, altitude, call_sign, measurement_time
FROM Aircraft a
WHERE EXISTS (
    SELECT * FROM Aircraft x
    WHERE x.flight_id = a.flight_id
    AND x.latitude = a.latitude 
    AND x.longitude = a.longitude
    AND x.altitude = a.altitude
    AND x.call_sign  = a.call_sign
    AND x.measurement_time = a.measurement_time 
    AND x.id < a.id
 )
;

如果上面的查询返回正确的行(被删除) 您可以将其更改为删除语句:


DELETE
FROM Aircraft a
WHERE EXISTS (
    SELECT * FROM Aircraft x
    WHERE x.flight_id = a.flight_id
    AND x.latitude = a.latitude 
    AND x.longitude = a.longitude
    AND x.altitude = a.altitude
    AND x.call_sign  = a.call_sign
    AND x.measurement_time = a.measurement_time 
    AND x.id < a.id
 )
;

【讨论】:

  • 所以这行得通,systemjack的回答也是如此。但是,您的答案会减少 79 行。我不明白为什么会这样。
  • 他认为 {flight_id, measurement_time} 会是一个很好的候选键。但可能有一些联系。
【解决方案2】:

如果是一次性操作,您可以创建一个具有相同架构的临时表,然后复制唯一的行,如下所示:

insert into Aircraft_temp
select distinct on (flight_id, measurement_time) Aircraft.* from Aircraft

然后通过重命名交换它们,或截断 Aircraft 并将临时内容复制回来 (truncate Aircraft; insert into Aircraft select * from Aircraft_temp;)。

将 Aircraft 重命名为 Aircraft_old 并将 Aircraft_temp 重命名为 Aircraft 更安全,这样您就可以保留原始数据,直到您确定一切正确为止。或者至少在执行截断之前检查上述计数查询中的行数是否与临时表中的行数相匹配。

更新 2:使用单独的有效主键(假设它称为 id),您可以像这样基于自联接执行删除:

delete from Aircraft using (
    select a1.id
    from Aircraft a1
    left join (select flight_id, measurement_time, min(id) as id from Aircraft group by 1,2) a2
    on a1.id = a2.id
    where a2.id is null
) as d
where Aircraft.id=d.id

这会找到每个航班的最小值 id(对于“最新”也可以做最大值),并识别完整集合中的所有记录,其中 id 不是最小值(连接中不匹配) .不匹配的 id 将被删除。

【讨论】:

  • 是的,我有一个主键id。它没有包含在 SQL 转储中,抱歉。此外,重复很好,这很正常,我希望会有重复。我想基本上对表进行清理。
  • 您可以在 flight_id 字段中添加一个 UNIQUE 约束,然后确保它不会在未来发生:stackoverflow.com/questions/469471/…
【解决方案3】:

我一直在 SQL SERVER 中使用 CTE 方法。这允许您定义要比较的列,一旦确定了哪些列构成重复,您就可以为其分配 CTE 值,然后返回并清除大于 1 的 CTE 值。这是一个我做的重复检查的例子。

WITH CTE AS
(select  d.UID
    ,d.LotKey
    ,d.SerialNo
    ,d.HotWeight
    ,d.MarketValue
    ,RN = ROW_NUMBER()OVER(PARTITION BY d.HotWeight, d.serialNo, d.MarketValue order by d.SerialNo)
from LotDetail d
where d.LotKey = ('1~20161019~305')
)
DELETE FROM CTE WHERE RN <> 1

在我的示例中,我正在查看 d.hotweight 和 d.serial no 匹配的 LotDetail 表。如果有匹配项,则原始的 CTE 为 1,任何重复的 CTE 为 2 或更高,具体取决于重复的数量。然后使用最后一个 DELETE 语句清除重复出现的条目。这真的很灵活,所以你应该能够适应你的问题。

这是一个根据您的情况量身定制的示例。

WITH CTE AS
(select  d.Flight_ID
    ,d.Latitude
    ,d.Longitude
    ,d.Altitude
    ,d.Call_sign
            ,d.Measurement*
    ,RN = ROW_NUMBER()OVER(PARTITION BY d.Flight_ID, d.Latitude, d.Longitude, d.Altitude, d.Call_Sign, d.Measurement* order by d.SerialNo)
from Aircraft d
where d.flight_id = ('**INSERT VALUE HERE')
)
DELETE FROM CTE WHERE RN <> 1

【讨论】:

  • 嗯,谢谢,但我不知道航班号。几乎有一百万行,因此每次都插入航班 ID 是不切实际的。这是你的本意,还是我错过了什么?
  • 啊,我错过了 postgres 部分。是的,这不是一个选择,但是要回答您的问题@Pookie,您不必知道 flight_id,这是您可以继续使用的一个示例。您实际上不必键入任何内容,但是如果您有这些带有日期戳的内容并且想要执行一系列日期,那么您可以使用它来代替。
  • @JoeResler 好的,感谢您的帮助和澄清 :)
猜你喜欢
  • 2019-05-29
  • 2017-07-06
  • 2021-06-24
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多