从数百万条记录中删除重复行的有效方法
Effective way to delete duplicate rows from millions of records
我正在寻找一种有效的方法来从我的数据库中删除重复的记录。首先,我使用了一个使用连接等的存储过程,这导致查询执行得非常慢。现在,我正在尝试一种不同的方法。请考虑以下查询:
/* QUERY A */
SELECT *
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
这个查询只用了 12 秒,结果是 182.400 条记录。 table 中的行数当前为 420.930.407,col1 和 col3 已编入索引。
下一个查询:
/* QUERY B */
WITH ALL_RECORDS AS
(SELECT id
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value)
SELECT *
FROM ALL_RECORDS
这个查询用了不到 2 秒,并为我提供了 table 中 182.400 条记录的所有 ID(根据 where 子句)。
然后,我的最后一个查询是 select 是在我要分组以检查重复项的列上分组的所有记录的最低(第一个)id 的查询:
/* QUERY C */
SELECT MIN(id)
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6
同样,此查询的执行时间不到 2 秒。结果是30.400,也就是说182.400条唯一记录中有30.400条唯一记录。
现在,我想删除(或者,首先 select 以确保我的查询正确)所有不唯一的记录。所以,我想从 my_table.
中删除 182.400 - 30.400 = 152.000 条记录
我想我会合并最后两个查询:根据 col1、col2 和 col3 的 where 子句获取属于我的数据集的所有 id(查询 B),然后 delete/select 来自该数据集的 ID 不在唯一记录 ID 的 ID 列表中(查询 C)。
然而,当我 select all from query B where query B.id NOT IN query C 时,查询不需要 2、4 或 12(14 或 16)秒,但似乎永远(1 分钟后显示 20.000 条记录,2 分钟后显示大约 40.000 条记录,所以我取消了查询,因为它会找到 152.000 条记录,这样需要 8 分钟)。
WITH ALL_RECORDS AS
(SELECT id
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value)
SELECT id
FROM ALL_RECORDS
WHERE id NOT IN
(SELECT MIN(id)
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6)
我知道 NOT IN
很慢,但我不明白它是怎么这么慢的(因为没有 not 部分的两个查询每个执行不到 2 秒)。
有没有人对我如何解决这个难题有一些好的建议?
----------------附加信息----------------
以前的解决方案是以下存储过程。出于某种原因,它在我的验收环境中完美执行,但在我的生产环境中却没有。目前,我们有超过 4 亿条生产记录和略多于 200 万条验收记录,所以这可能是一个原因。
DELETE my_table
FROM my_table
LEFT OUTER JOIN
(SELECT MIN(id) AS RowId,
col1,
col2,
col3,
col4,
col5,
col6
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6) AS KeepRows ON my_table.id = KeepRows.RowId
WHERE KeepRows.RowId IS NULL
AND my_table.col1 = value
AND my_table.col2 = value
AND my_table.col3 = value
我已将此解决方案基于 Whosebug 上的另一个答案(目前找不到),但我觉得我应该能够创建一个基于查询 B 和 C 的查询,该查询可在几秒钟内执行。 ..
将两个 2 秒查询组合在一起通常不会产生单个 4 秒查询,因为查询与其基础 tables 不同,很少被索引。
此类任务的通常方法是缓存 id
的你想保留在临时 table 中,相应地索引它,然后在 left join
中使用它(或 not in
- 我敢打赌最终的执行计划几乎是一样的)。
如果您在主 table 上使用索引,您可能会获得更多性能。例如,我认为 (col1, col2, col3)
应该给你的代码一些提升(不一定要按此顺序提及列,通常取决于它们的基数)。
with dupl as (
select row_number() over(partition by col1,col2,col3,col4,col5,col6 order by id) rn,
id,col1,col2,col3,col4,col5,col6
from myTable
)
delete dupl where rn>1
我正在寻找一种有效的方法来从我的数据库中删除重复的记录。首先,我使用了一个使用连接等的存储过程,这导致查询执行得非常慢。现在,我正在尝试一种不同的方法。请考虑以下查询:
/* QUERY A */
SELECT *
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
这个查询只用了 12 秒,结果是 182.400 条记录。 table 中的行数当前为 420.930.407,col1 和 col3 已编入索引。
下一个查询:
/* QUERY B */
WITH ALL_RECORDS AS
(SELECT id
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value)
SELECT *
FROM ALL_RECORDS
这个查询用了不到 2 秒,并为我提供了 table 中 182.400 条记录的所有 ID(根据 where 子句)。
然后,我的最后一个查询是 select 是在我要分组以检查重复项的列上分组的所有记录的最低(第一个)id 的查询:
/* QUERY C */
SELECT MIN(id)
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6
同样,此查询的执行时间不到 2 秒。结果是30.400,也就是说182.400条唯一记录中有30.400条唯一记录。
现在,我想删除(或者,首先 select 以确保我的查询正确)所有不唯一的记录。所以,我想从 my_table.
中删除 182.400 - 30.400 = 152.000 条记录我想我会合并最后两个查询:根据 col1、col2 和 col3 的 where 子句获取属于我的数据集的所有 id(查询 B),然后 delete/select 来自该数据集的 ID 不在唯一记录 ID 的 ID 列表中(查询 C)。
然而,当我 select all from query B where query B.id NOT IN query C 时,查询不需要 2、4 或 12(14 或 16)秒,但似乎永远(1 分钟后显示 20.000 条记录,2 分钟后显示大约 40.000 条记录,所以我取消了查询,因为它会找到 152.000 条记录,这样需要 8 分钟)。
WITH ALL_RECORDS AS
(SELECT id
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value)
SELECT id
FROM ALL_RECORDS
WHERE id NOT IN
(SELECT MIN(id)
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6)
我知道 NOT IN
很慢,但我不明白它是怎么这么慢的(因为没有 not 部分的两个查询每个执行不到 2 秒)。
有没有人对我如何解决这个难题有一些好的建议?
----------------附加信息----------------
以前的解决方案是以下存储过程。出于某种原因,它在我的验收环境中完美执行,但在我的生产环境中却没有。目前,我们有超过 4 亿条生产记录和略多于 200 万条验收记录,所以这可能是一个原因。
DELETE my_table
FROM my_table
LEFT OUTER JOIN
(SELECT MIN(id) AS RowId,
col1,
col2,
col3,
col4,
col5,
col6
FROM my_table
WHERE col1 = value
AND col2 = value
AND col3 = value
GROUP BY col1,
col2,
col3,
col4,
col5,
col6) AS KeepRows ON my_table.id = KeepRows.RowId
WHERE KeepRows.RowId IS NULL
AND my_table.col1 = value
AND my_table.col2 = value
AND my_table.col3 = value
我已将此解决方案基于 Whosebug 上的另一个答案(目前找不到),但我觉得我应该能够创建一个基于查询 B 和 C 的查询,该查询可在几秒钟内执行。 ..
将两个 2 秒查询组合在一起通常不会产生单个 4 秒查询,因为查询与其基础 tables 不同,很少被索引。
此类任务的通常方法是缓存 id
的你想保留在临时 table 中,相应地索引它,然后在 left join
中使用它(或 not in
- 我敢打赌最终的执行计划几乎是一样的)。
如果您在主 table 上使用索引,您可能会获得更多性能。例如,我认为 (col1, col2, col3)
应该给你的代码一些提升(不一定要按此顺序提及列,通常取决于它们的基数)。
with dupl as (
select row_number() over(partition by col1,col2,col3,col4,col5,col6 order by id) rn,
id,col1,col2,col3,col4,col5,col6
from myTable
)
delete dupl where rn>1