【问题标题】:SQL: Delete rows in a table where one field's value is lesser than group averageSQL:删除表中一个字段的值小于组平均值的行
【发布时间】:2020-05-20 21:58:14
【问题描述】:

现在,我首先运行以下查询:

select group_name, avg(numeric_field) as avg_value, count(group_name) as n from table_name group by group_name order by n desc;

假设我得到输出:

group_name     | avg_value | n
----------------------------------------
nice_group_name| 1566.353  | 2034
other_group    | 235.43    | 1390
.
.
. 

然后,我将手动对每个组使用以下查询逐个删除每个组中的记录:

delete from table_name where group_name = 'nice_group_name' and numeric_field < 1567;

这里的 1567 是 avg_valuenice_group_name 的近似值。

如何自动对第一个查询结果的所有行运行第二个查询?

【问题讨论】:

    标签: sql sqlite subquery


    【解决方案1】:

    您可以使用相关子查询:

    delete from table_name
        where numeric_field < (select avg(t2.numeric_field)
                               from table_name t2
                               where t2.group_name = table_name.group_name
                              );
    

    为了提高性能,您需要在tablename(group_name, numeric_field) 上建立索引。

    如果您的群组较少,您可能会发现这样更有效:

    with a as (
          select group_name, avg(numeric_field) as anf
          from table_name
          group by group_name
         )
    delete from table_name
        where numeric_field < (select a.anf from a where a.group_name = table_name.group_name);
    

    【讨论】:

    • 我尝试用select 替换delete 查询。这需要不可接受的长时间。以至于我不得不强制杀死sqlitebrowser 进程并以损坏的数据库映像结束。不过我有备份。
    • @pii_ke 。 . .这并不奇怪。平均而言,您将删除表中的一半行。重新创建表通常更有效。但是,这不是您问的问题。您刚刚询问了有关修复 delete 查询的问题。如果您需要其他解决方案,请提出一个新问题,其中包含示例数据、所需结果以及您想要做什么的说明。
    • 没关系。这个查询花了很长时间,以至于我开始怀疑这是否是不必要地重复每次删除检查的平均值计算。而大量的删除只是问题的一小部分。
    • @pii_ke 。 . .它正在重复平均计算;但是,删除的代价是昂贵的。也就是说,如果您有索引并且您的组不是很大,那应该不是一个大问题。如果您只有少数几个组,问题将是。
    • 感谢第二个解决方案,它对我来说效果很好(不到 30 秒)。我的表有大约 120,000 行和 570 个组。第一个解决方案甚至在 6 分钟内也没有完成。我在上述列上没有索引。
    【解决方案2】:

    如果table_name 有一些主键字段(比如id),则使用以下内容:

    alter table table_name rename to bak;
    create temp table avg_val as
        select group_name as g, avg(numeric_field) as a from bak
        group by group_name;
    create table table_name as
        select * from bak where id in (
            select bak.id from
            avg_val join bak on bak.group_name = avg_val.g
            where avg_val.a <= bak.numeric_field
    );
    

    检查table_name。如果一切顺利,您可以删除备份的旧表:

    drop table bak;
    

    简单来说,步骤是:

    1. 重命名原始表
    2. 为每个组创建一个平均值的临时表
    3. 使用原始表中的所有行创建一个新表,其中 numeric_field 小于该组的平均值。
    4. 删除重命名的原始表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 2016-11-10
      • 1970-01-01
      • 1970-01-01
      • 2015-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多