0

Я ищу, чтобы найти эффективный способ удаления дублированных записей из моей базы данных. Во-первых, я использовал хранимую процедуру, которая использует объединения и т. Д., Что заставило запрос выполнить очень медленно. Теперь я пытаюсь использовать другой подход. Пожалуйста, обратите внимание на следующие вопросы:Эффективный способ удаления повторяющихся строк из миллионов записей

/* QUERY A */ 

SELECT * 
FROM my_table 
WHERE col1 = value 
    AND col2 = value 
    AND col3 = value 

Этот запрос просто выполняется в течение 12 секунд, с результатом 182.400 записей. Количество строк в таблице в настоящее время 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 секунд, и дает мне все корочки 182.400 записей в таблице (в соответствии с пунктом где).

Тогда мой последний запрос, запрос, который выбирает самый низкий (первый) идентификатор всех записей, сгруппированных по столбцам, которые я хочу, чтобы группа на проверки дубликатов:

/* 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, что означает, что есть уникальные записи из 30.400 уникальных записей из 182.400 записей.

Теперь, я хотел бы удалить (или, сначала выберите, чтобы убедиться, что у меня есть правильный запрос) все записи, которые не уникальны. Итак, я хотел бы удалить 182.400 - 30.400 = 152.000 записей из my_table.

Я думал, что объединить два последних запроса: получить все идентификаторы, принадлежащие моему набору данных, в соответствии с предложением where в col1, col2 и col3 (запрос B), а затем удалить/выбрать все записи из этого набора данных который id не находится в списке идентификаторов уникальной записи (запрос C).

Однако, когда я выбираю все из запроса B, где запрос B.id NOT IN query C, запрос не принимает 2, 4 или 12 (14 или 16) секунд, но, кажется, берется навсегда (20 000 записей, показанных после 1 минута, около 40.000 через 2 минуты, поэтому я отменил запрос, так как он найдет 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 медленно, но я не могу понять, как именно это медленно (так как запросы без кнопки не частично выполнить менее чем за 2 секунды каждый).

Есть ли у кого-нибудь хороший совет для меня, как решить эту загадку?

------------------ Дополнительная информация ------------------

Предыдущее решение было следующий хранимая процедура. По какой-то причине он отлично работает в моей среде принятия, но не в моей производственной среде. В настоящее время у нас более 400 миллионов записей о производстве и чуть более 2 миллионов записей о принятии, поэтому это может быть причиной.

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 

Я основывал это решение на другой ответ на StackOverflow (не может найти его в данный момент), но я чувствую, что я должен быть в состоянии создать запрос, основанный на Query B и C, который выполняется в течение нескольких секунды ...

ответ

1
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 
+0

Это, кажется, работает очень быстро, спасибо! Я постараюсь реализовать его завтра, и, надеюсь, он будет работать и на производстве :) – Tjab

1

Объединение двух двухсекундных запросов вместе не приведет, как правило, к одному 4-секундному запросу, поскольку запросы, в отличие от их базовых таблиц, редко индексируются.

Обычный подход для такого рода задач является кэширование id «S вы хотите сохранить во временную таблицу, индекс его соответствующим образом, а затем использовать его в left join (или not in - Держу пари, в результате планы выполнения практически одинаковы).

Возможно, вы можете получить более высокую производительность, если будете играть с индексами на главной таблице. Например, я думаю, что (col1, col2, col3) должен дать вашему коду некоторый импульс (колонки не обязательно должны упоминаться в этом порядке, это обычно зависит от их мощности).

+0

Благодарим вас за этот комментарий. Хотя решение Alex работало и намного проще кэширования во временной таблице, оно дает мне лучшее представление о том, как подходить к запросам. – Tjab

Смежные вопросы