表中分组的列仅保留最后两行

时间:2019-04-10 06:12:03

标签: mysql group-by sql-delete sqlperformance

我有一个约30万行的“历史”表,每天都有新数据填充。我只想保留每个refSchema / refId组合的最后两行。

实际上我是这样的:

第一步:

SELECT refSchema,refId FROM History GROUP BY refSchema,refId

通过此语句,我得到了所有组合(大约40.000)。

第二步:

我运行一个foreach,以查找上面查询的现有行,如下所示:

SELECT id
FROM History
WHERE refSchema = ? AND refId = ? AND state = 'done'
ORDER BY importedAt 
DESC LIMIT 2,2000

请记住,我要保留表中的最后两行,因此我限制为22000。如果找到匹配的行,则将 id 放入名为idList的数组中。

最终步骤

我以这种方式删除数组中的所有ID:

DELETE FROM History WHERE id in ($idList)

这似乎并不是最好的性能,因为我必须检查每个组合并附加查询。有没有办法让一个delete语句神奇地避免40.000个额外的查询?

编辑更新:我使用AWS Aurora DB

1 个答案:

答案 0 :(得分:0)

如果您使用的是MySQL 8+,则从概念上讲,此处进行的一种简单方法是使用CTE标识要保留的每个组的前两行。然后,删除其架构/标识对不是出现在此白名单中的所有记录:

WITH cte AS (
    SELECT refSchema, refId
    FROM
    (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY refSchema, refId ORDER BY importedAt DESC) rn
        FROM History
    ) t
    WHERE rn IN (1, 2)
)

DELETE
FROM History
WHERE (refSchema, refId) NOT IN (SELECT refSchema, refId FROM cte);

如果您不能使用CTE,请尝试内联以上CTE:

DELETE
FROM History
WHERE (refSchema, refId) NOT IN (
    SELECT refSchema, refId
    FROM
    (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY refSchema, refId ORDER BY importedAt DESC) rn
        FROM History
    ) t
    WHERE rn IN (1, 2)
);