【问题标题】:oracle sql - delete rows with null value for every grouporacle sql - 为每个组删除具有空值的行
【发布时间】:2014-01-22 21:22:53
【问题描述】:

我尝试删除邮件列中具有空值的行,当且仅当存在具有相同 person_id 的非空值的行。

DELETE
FROM
  EMP_MAIL EM
WHERE
  MAIL IS NULL
AND EXISTS
  (
    SELECT 
      EMP_ID
    FROM
      EMP_MAIL EM2
    WHERE
      MAIL IS NOT NULL
    AND EM.EMP_ID    = EM2.EMP_ID
  ) ;

但这需要相当长的时间。 (我在 EMP_MAIL(EMP_ID) 上有一个非唯一索引)

有没有更好的方法来执行这个操作?

【问题讨论】:

  • 定义“相当一段时间”。表中有多少行?你要删除多少行?表有多大(以 MB/GB/ 等为单位)?查询计划是什么?
  • 表中有 3M 行。 0 行要删除。 30 秒。 2 全表访问。
  • 查询很好。 3M 行 30 秒还不错。但是 EMP_ID 在表中重复 .. 为什么不将 EMP_ID 设为 PK,如果 MAIL 列必须存储多个值,它们有一个名为 MAIL 的单独表,其中包含 emp_id、mail 列,然后您可以根据需要加入它们。跨度>
  • 你能解释一下你的逻辑规则吗?
  • @Hamidreza - 你是什么意思?我确实为员工设置了不同的表,但出于设计原因,我为每个员工保留一行,如果没有邮件,则输入一个空值。

标签: sql performance oracle


【解决方案1】:

好吧,我已经测试了一些,现在我将展示结果。

这是我的测试脚本(看看:根本没有索引):

create table emp_mail (
  emp_id        number,
  mail          clob
);

declare
  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 90;
begin
  for i in 1..300 loop
    insert into emp_mail
    select round(dbms_random.value*amount_of_users),case when dbms_random.value < nulls_amount_percent/100 then null else 'some mail' end
    from dual
    connect by level <= 10000;

    commit;
  end loop;
end;
/
delete from emp_mail em
where mail is null
  and exists (select null
              from emp_mail em2
              where mail is not null
                and em.emp_id = em2.emp_id);

drop table emp_mail;

它创建表(我用 CLOB 创建最坏的情况),然后用常量填充表 3M 行

  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 90;

您可以围绕将要删除的行进行操作,然后它会执行您的“删除”语句并删除表。

让我们谈谈数字。

警告:当我谈到秒时,我的意思是只删除语句,因为行生成器匿名块需要将近 2 分钟。

有了这些值

  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 90;

几乎整张桌子都被删除了,我可以重现你神秘的 27.5 秒。

那我拿

  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 1;

减少删除行的数量,它只给了我 1.8 秒。

我们来看一个没有删除行的情况:

  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 0;

猜时间? 1.1 秒,据你说是你的情况。

好吧,我不能给你任何建议,因为你所说的事情可能比一个查询更具全局性。也许,你的数据库版本太旧了,或者它是关于硬件或其他什么的。

如果是关于这个案子

  amount_of_users        constant number := 30000;
  nulls_amount_percent   constant number := 90;

当确实有很多行被删除时,我可以建议将删除拆分为例如10000-rows-parts(首先选择 count(*) 以了解您应该运行多少次受限删除)并在每次之后进行提交,有时它比删除大量行要快得多。但据您所说,您删除了 0 行,这不应该是您的问题。

【讨论】:

    【解决方案2】:

    这取决于你的输入数据,我希望这个查询可以帮助你或给你一些解决问题的线索:

    DELETE
    from 
    EMP_MAIL t1 
    where 
    t1.MAIL is null and 
    not Exists
    (select t2.EMP_ID from (select EMP_ID from EMP_MAIL where mail is not null
    minus
    select EMP_ID from EMP_MAIL where mail is null)t2 where t1.EMP_ID =t2.EMP_ID);
    

    【讨论】:

    • 我猜这会导致三个全表扫描(需要检查解释计划),我会投票给 OP 的查询。
    猜你喜欢
    • 2018-12-19
    • 2019-07-30
    • 1970-01-01
    • 1970-01-01
    • 2019-08-13
    • 2021-07-30
    • 2021-11-17
    • 1970-01-01
    • 2022-01-12
    相关资源
    最近更新 更多