【问题标题】:Only select first row of repeating value in a column in SQL只选择 SQL 列中重复值的第一行
【发布时间】:2011-12-30 20:06:13
【问题描述】:

我有一个表,该表有一列可能在突发中具有相同的值。像这样:

+----+---------+
| id |   Col1  | 
+----+---------+
| 1  | 6050000 |
+----+---------+
| 2  | 6050000 |
+----+---------+
| 3  | 6050000 |
+----+---------+
| 4  | 6060000 |
+----+---------+
| 5  | 6060000 |
+----+---------+
| 6  | 6060000 |
+----+---------+
| 7  | 6060000 |
+----+---------+
| 8  | 6060000 |
+----+---------+
| 9  | 6050000 |
+----+---------+
| 10 | 6000000 |
+----+---------+
| 11 | 6000000 |
+----+---------+

现在我想修剪 Col1 的值重复的行,并且只选择第一次出现的行。
对于上表,结果应该是:

+----+---------+
| id |   Col1  | 
+----+---------+
| 1  | 6050000 |
+----+---------+
| 4  | 6060000 |
+----+---------+
| 9  | 6050000 |
+----+---------+
| 10 | 6000000 |
+----+---------+

如何在 SQL 中执行此操作?
请注意,只应删除突发行,并且可以在非突发行中重复值! id=1 & id=9 在样本结果。

编辑:
我用这个实现了它:

select id,col1 from data as d1
where not exists (
    Select id from data as d2
    where d2.id=d1.id-1 and d1.col1=d2.col1 order by id limit 1)

但这仅适用于 id 是连续的。由于 id 之间的间隙(已删除的),查询会中断。我该如何解决这个问题?

【问题讨论】:

  • id 是否保证是连续的(没有间隙或重复)?
  • 所以你用的是SQlite和MS Access,不能用row_number()这样的窗口函数,对吧?
  • @ErwinBrandstetter:是的,我使用的是 SQlite 和 MS Access,当我搜索它们不支持 LAG 和 LEAD 时,我没有找到任何像 row_numbers 这样的窗口函数。

标签: sql sqlite ms-access duplicates


【解决方案1】:

您可以使用EXISTS 半联接来识别候选人:

选择想要的行:

SELECT * FROM tbl t
WHERE  NOT EXISTS (
    SELECT *
    FROM   tbl
    WHERE  col1 = t.col1
    AND    id = t.id - 1
    )
ORDER  BY id;

删除不需要的行:

DELETE FROM tbl AS t
-- SELECT * FROM tbl t  -- check first?
WHERE EXISTS (
    SELECT *
    FROM   tbl
    WHERE  col1 = t.col1
    AND    id   = t.id - 1
    );

这有效地删除了每一行,其中前一行在col1 中具有相同的值,从而达到您设定的目标:每次爆发只有第一行存活。

我留下了已评论的 SELECT 声明,因为您应该始终在执行此操作之前检查将要删除的内容。

非顺序 ID 的解决方案:

如果您的 RDBMS 支持 CTEswindow functions(如 PostgreSQL、Oracle、SQL Server ......但 SQLite prior to v3.25、MS Access 或 MySQL prior to v8.0.1),则是一种优雅的方式:

WITH cte AS (
    SELECT *, row_number() OVER (ORDER BY id) AS rn
    FROM   tbl
    )
SELECT id, col1
FROM   cte c
WHERE  NOT EXISTS (
    SELECT *
    FROM   cte
    WHERE  col1 = c.col1
    AND    rn   = c.rn - 1
    )
ORDER  BY id;

另一种完成工作的方式没有这些细节(应该适合你):

SELECT id, col1
FROM   tbl t
WHERE  (
    SELECT col1 = t.col1
    FROM   tbl
    WHERE  id < t.id
    ORDER  BY id DESC
    LIMIT  1) IS NOT TRUE
ORDER  BY id;

【讨论】:

  • 嗨,谢谢,它可以开发成覆盖不连续的 id 吗? (使用existsorder
  • @Snigger:我猜。请定义“不连续的 id”。最好通过编辑您的问题让每个人以一种不错的格式阅读。啊,你已经做到了。应该可以,谢谢。
  • 第二个查询运行良好,只是它没有选择第一行(这始终是第一次出现),所以我编辑了您的查询,如下所示:SELECT id, col1 FROM tbl WHERE ( SELECT t.col1 = tbl.col1 FROM tbl AS t WHERE t.id &lt; tbl.id ORDER BY id DESC LIMIT 1)==0 OR NOT exists(SELECT tt.id FROM tbl AS tt WHERE tt.id &lt; tbl.id) ORDER BY id for SQlite
【解决方案2】:
select min(id), Col1 from tableName group by Col1 

【讨论】:

  • 这会删除所有重复的行,但是如果您检查我的示例结果,您会看到 id 1 和 id 9 对于 Col1 都有相同的值,但两者都在结果中,因为那里有另一个值(6060000 on id 4 到 id 8) 之间。
【解决方案3】:

如果您的 RDBMS 支持窗口聚合函数和/或 LEAD() 和 LAG() 函数,您可以利用它们来完成您要报告的内容。以下 SQL 将帮助您走上正确的道路:

SELECT id
     , Col AS CurCol
     , MAX(Col)
       OVER(ORDER BY id ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS PrevCol
     , MIN(COL)
       OVER(ORDER BY id ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS NextCol
FROM MyTable

从那里您可以将该 SQL 放入带有一些 CASE 逻辑的派生表中,如果 NextColPrevColCurCol 相同,则设置 CurCol = NULL。然后就可以collapse消除所有id记录CurCol IS NULL

如果您无法使用窗口聚合或 LEAD/LAG 函数,您的任务会稍微复杂一些。

希望这会有所帮助。

【讨论】:

  • 嗨,谢谢,但我使用 SQlite 和 MS Access 作为 DBMS,他们没有这个能力
【解决方案4】:

由于id 始终是连续的,没有间隙或重复,根据您的评论,您可以使用以下方法:

SELECT t1.*
FROM atable t1
  LEFT JOIN atable t2 ON t1.id = t2.id + 1 AND t1.Col1 = t2.Col1
WHERE t2.id IS NULL

在左侧的id 比右侧大一并且它们的Col1 值相同的条件下,该表与自身(外部)连接。换句话说,条件是“前一行包含与当前行相同的Col1 值”。如果右侧没有匹配项,则应选择当前记录。


更新

要考虑非顺序的ids(但是,假定它是唯一的并定义了Col1 的更改顺序),您还可以尝试以下查询:

SELECT t1.*
FROM atable t1
  LEFT JOIN atable t2 ON t1.id > t2.id
  LEFT JOIN atable t3 ON t1.id > t3.id AND t3.id > t2.id
WHERE t3.id IS NULL
  AND (t2.id IS NULL OR t2.Col1 <> t1.Col1)

第三个自连接是为了确保第二个产生直接在t1 之前的行。也就是说,如果t3 没有匹配,那么t2 要么包含前一行,要么没有匹配,后者意味着t1 的当前行是最上面的。

【讨论】:

  • 嗨,谢谢,它可以开发成覆盖不连续的 id 吗? (使用existsorder
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 2016-07-22
  • 2019-12-17
  • 2016-02-11
  • 2013-05-07
相关资源
最近更新 更多