Удалить дубликаты записей в таблице без индекса, используя postgresql - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть таблица, как показано ниже

Subject_id  subject_name  Standard Rank Previous_subject_id
13              ABC            1st    1          21
13              ABC            1st    1          23   
13              ABC            1st    1          13
25              def            3rd    6          42   
25              def            3rd    6          25
25              def            3rd    6          28
25              XYZ            2nd    7          26
29              PQR            1st    1          31         

Как видно, все столбцы и значения одинаковы, кроме столбца previous_subject_id (для одной строки).

правило 1

Если после правила 1 все еще есть дубликаты, я бы хотел удалить всех, кто удовлетворяет условию subject_id = previous_subject_id?

правило 2

Если все еще есть повторяющиеся subject_ids, сохраните только первую (встречающуюся) запись

Как вы можете видеть в приведенном ниже примере вывода, который у меня есть сохраняется только первая запись.

Я ожидаю, что результат будет таким, как показано ниже

Subject_id  subject_name  Standard Rank Previous_subject_id
13              ABC            1st    1          21
25              def            3rd    6          42
25              XYZ            2nd    7          26
29              PQR            1st    1          31  

Единственная проблема - моя таблица имеет 285000 записей и не проиндексирована. После удаления записей я смогу установить индекс на subject_id, поскольку они становятся уникальными.

Это то, что я пытался

select * from subject_class a
inner join 
subject_class b
on a.subject_id = b.previous_subject_id

Хотя приведенный выше запрос продолжает выполняться для долгое время из-за проблем с индексами, есть ли эффективный подход?

Но как мне их отбросить?

Можете ли вы помочь мне с этим, пожалуйста?

1 Ответ

1 голос
/ 01 апреля 2020

Я не понимаю, почему вы используете JOIN, когда это выглядит так просто:

DELETE FROM subject_class WHERE subject_id = previous_subject_id?

Кроме того, 285 000 строк - это не большое количество, и производительность должна быть в порядке. Однако 285 000 * 285 000 (81 млрд.) - это большое число, и это, по сути, то, с чем должен работать ваш запрос с JOIN.


Хорошо, теперь у нас проблема. В реляционной базе данных нет понятия «первый» или «последний». Строки не имеют какого-либо внутреннего порядка, если вы не скажете им что-то для заказа. В вашем примере вы визуально выбрали две строки, чтобы исключить их из списка исключительно на том основании, что когда вы их перечисляете, это порядок, в котором они появляются. Однако этот порядок совершенно недетерминирован c. На самом деле это, скорее всего, тот порядок, в котором данные были вставлены в кучу (неиндексированная таблица), но это практически невозможно воспроизвести, и это выходит за рамки этого вопроса.

Что я могу сделать, так это предоставить детерминированный c способ удаления строк. Поскольку это более сложно, я настрою некоторые тестовые данные:

DECLARE @subject_class TABLE (
    subject_id INT,
    subject_name VARCHAR(20),
    [standard] VARCHAR(20),
    [rank] INT,
    previous_subject_id INT);
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 21;
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 23;
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 13;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 42;  
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 25;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 28;

Это в основном ваши настройки, данные, которые вы перечислили в таблице без индекса.

Первая часть прост:

DELETE FROM @subject_class WHERE subject_id = previous_subject_id; --fixes 2 records

Вторая часть немного сложнее, поэтому я использую выражение общей таблицы:

WITH cte AS (
    SELECT
        subject_id,
        MIN(previous_subject_id) AS min_previous_subject_id
    FROM
        @subject_class
    GROUP BY
        subject_id)
DELETE
    s
FROM
    @subject_class s
    INNER JOIN cte c ON c.subject_id = s.subject_id AND c.min_previous_subject_id != s.previous_subject_id;
SELECT * FROM @subject_class;

Это работает, сначала определив минимум previous_subject_id для каждого subject_id и предполагая, что это единственный, который мы хотим сохранить. Есть много других способов сделать это, вы можете выбрать наибольшее значение или придумать более сложное правило.

Это не даст вам того, о чем вы просили, вместо этого вы получите результат:

subject_id  subject_name    standard    rank    previous_subject_id
13          ABC             1st         1       21
25          def             3rd         6       28

Однако это определенно c, поскольку вы будете получать один и тот же результат при каждом выполнении запроса.


Вы хотели, чтобы запрос удалял только те строки, где было найдено совпадение в «других» полях, так что вот так:

DECLARE @subject_class TABLE (
    subject_id INT,
    subject_name VARCHAR(20),
    [standard] VARCHAR(20),
    [rank] INT,
    previous_subject_id INT);
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 21;
INSERT INTO @subject_class SELECT 13, 'ABF', '1st', 1, 23;
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 13;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 42;  
INSERT INTO @subject_class SELECT 25, 'dez', '3rd', 6, 25;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 28;

DELETE FROM @subject_class WHERE subject_id = previous_subject_id;

WITH cte AS (
    SELECT
        subject_id,
        subject_name,
        [standard],
        [rank],
        MIN(previous_subject_id) AS min_previous_subject_id
    FROM
        @subject_class
    GROUP BY
        subject_id,
        subject_name,
        [standard],
        [rank])
DELETE
    s
FROM
    @subject_class s
    INNER JOIN cte c ON c.subject_id = s.subject_id 
        AND c.subject_name = s.subject_name 
        AND c.[standard] = s.[standard]
        AND c.[rank] = s.[rank]
WHERE
    c.min_previous_subject_id != s.previous_subject_id;

SELECT * FROM @subject_class;

На этот раз мы в итоге получим 3 строки: - строка для «dez» все еще удаляется на основании того, что она имеет одинаковые subject_id и previous_subject_id; - строка для "ABF" сохраняется, поскольку она не соответствует имени субъекта.


На этот раз с вашими обновленными данными:

DECLARE @subject_class TABLE (
    subject_id INT,
    subject_name VARCHAR(20),
    [standard] VARCHAR(20),
    [rank] INT,
    previous_subject_id INT);
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 21;
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 23;
INSERT INTO @subject_class SELECT 13, 'ABC', '1st', 1, 13;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 42;  
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 25;
INSERT INTO @subject_class SELECT 25, 'def', '3rd', 6, 28;
INSERT INTO @subject_class SELECT 25, 'XYZ', '2nd', 7, 26;
INSERT INTO @subject_class SELECT 29, 'PQR', '1st', 1, 31;

DELETE FROM @subject_class WHERE subject_id = previous_subject_id;

WITH cte AS (
    SELECT
        subject_id,
        subject_name,
        [standard],
        [rank],
        MIN(previous_subject_id) AS min_previous_subject_id
    FROM
        @subject_class
    GROUP BY
        subject_id,
        subject_name,
        [standard],
        [rank])
DELETE
    s
FROM
    @subject_class s
    INNER JOIN cte c ON c.subject_id = s.subject_id 
        AND c.subject_name = s.subject_name 
        AND c.[standard] = s.[standard]
        AND c.[rank] = s.[rank]
WHERE
    c.min_previous_subject_id != s.previous_subject_id;

SELECT * FROM @subject_class;

Я получаю Результаты:

subject_id  subject_name    standard    rank    previous_subject_id
13          ABC             1st         1       21
25          def             3rd         6       28
25          XYZ             2nd         7       26
29          PQR             1st         1       31

Что соответствует тому, что вы ожидали? Ну, не совсем, но это потому, что вы все еще используете «первое», когда такой концепции нет. Я получаю одинаковое количество строк, и результаты в основном одинаковы. Я просто выбираю другой ряд, чтобы сохранить, чем вы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...