【问题标题】:Deleting duplicate rows in a database without using rowid or creating a temp table在不使用 rowid 或创建临时表的情况下删除数据库中的重复行
【发布时间】:2018-04-24 17:37:25
【问题描述】:

多年前,我在一次电话采访中被要求删除数据库中的重复行。在给出了几个可行的解决方案后,我最终被告知限制是:

  • 假设表有一个 VARCHAR 列
  • 不能使用rowid
  • 不能使用临时表

面试官拒绝给我答案。从那以后我就被难住了。

多年来询问了几位同事后,我确信没有解决方案。我错了吗?!

【问题讨论】:

  • 呃。相信我。反正你也不想要那份工作。他们让你像这样把双手绑在背后的问题通常更多地是为了表明面试官没有测试候选人有多聪明。
  • 感谢 JohnFx 的支持...让我更高兴我没有从事那份工作。
  • 你最不想要的就是一个不想要解决方案的老板,他/她想要他们的解决方案。尝试雇用自己的克隆人是一个新手经理的错误,并且非常自恋。

标签: sql database


【解决方案1】:

如果你确实有答案,是否会突然出现新的限制?由于您提到 ROWID,我假设您使用的是 Oracle。解决方案适用于 SQL Server。

灵感来自 SQLServerCentral.com http://www.sqlservercentral.com/scripts/T-SQL/62866/

while(1=1) begin
  delete top (1)
  from MyTable
  where VarcharColumn in 
    (select VarcharColumn
    from MyTable
    group by VarcharColumn
    having count(*) > 1)

    if @@rowcount = 0
      exit
end

一次删除一行。当一组重复项的倒数第二行消失时,剩余的行将不会在下一次通过循环时出现在子选择中。 (太糟糕了!)

另外,请参阅http://www.sqlservercentral.com/articles/T-SQL/63578/ 以获取灵感。 RBarry Young 提出了一种可以修改的方法,将去重数据存储在同一个表中,删除所有原始行,然后将存储的去重数据转换回正确的格式。他有三列,所以与你正在做的不完全相似。

然后它可以用光标来做。不确定,也没有时间查。但是创建一个游标以按顺序从表中选择所有内容,然后创建一个变量来跟踪最后一行的样子。如果当前行相同,则删除,否则将变量设置为当前行。

【讨论】:

  • 你是对的……对那个 while 循环来说真是太糟糕了。当我建议使用 PL/SQL 过程时,确实出现了一个新的限制,这也是对的。他只希望使用 DELETE 语句来完成。
  • 嘿,是我!仅供参考,我在我的文章中使用的技术也适用于单个 VARCHAR 列,只要它没有在任何地方被最大化。哦,那是没有任何循环或游标(这使得它有点容易)和 SQL 2000 兼容,所以也没有 Row_Number() 函数。是的,这该死很难,但可以做到。
【解决方案2】:

这是一种完全自大的方法,但考虑到愚蠢的要求,假设 SQL 2005 或更高版本是一个可行的解决方案:

  DELETE from MyTable
  WHERE ROW_NUMBER() over(PARTITION BY [MyField] order by MyField)>1

【讨论】:

  • 有趣 - 听起来 row_number() 与 rowid 非常相似
  • @vh row_number() 比 Oracle 的 ROWID 更类似于 Oracle 的 ROWNUM,但更灵活。 Oracle 也有 ROW_NUMBER()。它是分析函数的一部分。哦,这在 SQL Server 2005/2008 中不起作用,因为 where 子句中不允许使用 ROW_NUMBER()。
  • 确认!你说的对。我发誓这在我昨天测试时有效,但可惜今天早上它不起作用。对不起虚假的希望。另外,我刚刚注意到 noRowID 约束,所以这可能违反了条款的精神。我说你应该反问面试官,他们如何在没有键盘或鼠标的情况下编写查询来做到这一点。这简直是​​荒谬的。
  • 对于 SQL Server 2005+,您可以使用 CTE 使 ROW_NUMBER() 结果可用于 DELETE 语句。 ;WITH x AS (SELECT ROW_NUMBER() OVER (PARTITION BY MyField ORDER BY MyField) as RN, * FROM MyTable) DELETE x WHERE RN > 1
【解决方案3】:

我会在 VARCHAR 列中为重复的行放置一个固定大小的唯一编号,然后解析出该数字并删除除最小行之外的所有行。也许这就是他的 VARCHAR 约束的用途。但这很糟糕,因为它假设您的唯一号码适合。蹩脚的问题。反正你也不想在那里工作。 ;-)

【讨论】:

  • $chars = array('L', 'O'); while(1=1){ echo $chars[0];回声 $chars[1];} 回声 $chars[0];
【解决方案4】:

假设您正在为 SQL 引擎实现 DELETE 语句。如何从表中删除完全相同的两行?你需要一些东西来区分一个和另一个! 在以下约束(提供给您)下,您实际上不能删除完全重复的行(所有列都相等)

  1. 不使用 ROWID 或 ROWNUM
  2. 没有临时表
  3. 无程序代码

但是,即使其中一个条件被放宽,也可以这样做。以下是至少使用三个条件之一的解决方案

假设表定义如下

创建表 t1 (
col1 vacrchar2(100),
col2 编号(5),
col3 编号(2)
);

重复行标识:

选择 col1、col2、col3
从 t1
按 col1、col2、col3 分组
计数(*) >1

也可以使用以下方法识别重复行: select c1,c2,c3, row_number() over (partition by (c1,c2,c3) order by c1,c2,c3) rn
from t1

注意:至少在 Oracle 10g 中,JohnFx 建议在 DELETE 语句中不能使用 row_number() 分析函数。

  • 使用 ROWID 的解决方案

从 t1 中删除 row_id >
( select min(t1_inner.row_id) from t1 t1_innner
where t1_inner.c1=t1.c1 and t1_inner.c2=t1.c2 and t1_inner.c3=t1.c3 ))

  • 使用临时表的解决方案

创建表 t1_dups 为 (
//在此处编写查询以查找上面列出的重复行//
)

从 t1 中删除
其中 t1.c1,t1.c2,t1.c3 在(从 t1.dups 中选择 *)
插入 t1(
从 t1_dups 中选择 c1,c2,c3)

  • 使用程序代码的解决方案

这将使用类似于我们使用临时表的方法。

【讨论】:

    【解决方案5】:
    create table temp as 
    select c1,c2 
    from table 
    group by c1,c2 
    having(count(*)>1 or count(*)=1);
    

    现在删除基表。 将临时表重命名为基表。

    【讨论】:

      【解决方案6】:

      使用此查询解决了我的问题: 从哪里删除(通过计数(*)> 1从组中选择) 在 PLSQL 中

      【讨论】:

        猜你喜欢
        • 2020-02-29
        • 1970-01-01
        • 2018-05-10
        • 2011-04-11
        • 2014-11-11
        • 1970-01-01
        • 2012-11-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多