Сравнение таблиц в PostgreSQL - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть таблица:

CREATE TABLE my_schema.my_data
(
    id character varying COLLATE pg_catalog."default" NOT NULL,
    name character varying COLLATE pg_catalog."default" NOT NULL,
    length numeric(6,4),
    width numeric(6,4),
    rp numeric(4,2),
    CONSTRAINT id_pkey PRIMARY KEY (id)
);

и временная таблица:

CREATE TEMPORARY TABLE new_data (LIKE my_schema.my_data);

Затем временная таблица заполняется более новой версией набора данных, который существует в таблице my_data.

Я пытаюсь идентифицировать записи во временной таблице, которые имеют тот же первичный ключ, что и существующая запись в таблице my_data, но имеют хотя бы одно другое значение, отличающееся.

Мой текущий метод - выполнить запрос, подобный следующему примеру:

SELECT temp.id 
FROM (SELECT * FROM my_schema.my_data WHERE my_data.id IN ('X2025','X8716','X4091','X2443','X8922','X5929','X3016','X3036','X4829','X9578')) AS orig 
RIGHT JOIN (SELECT * FROM pg_temp.new_data WHERE new_data.id IN ('X2025','X8716','X4091','X2443','X8922','X5929','X3016','X3036','X4829','X9578')) AS temp 
ON (orig.id = temp.id OR (orig.id IS NULL AND temp.id IS NULL))
AND (orig.name = temp.name OR (orig.name IS NULL AND temp.name IS NULL))
AND (orig.length = temp.length OR (orig.length IS NULL and temp.length IS NULL))
AND (orig.width = temp.width OR (orig.width IS NULL and temp.width IS NULL))
AND (orig.rp = temp.rp OR (orig.rp IS NULL and temp.rp IS NULL)) 
WHERE orig.id IS NULL;

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

Есть ли предложения по более эффективному выявлению записей, которые отличаются?

UPDATE:

У меня есть набор данных, который регулярно обновляется. К сожалению, я получаю полный набор данных каждый раз вместо только новых или обновленных записей. (Я работаю над исправлением этого процесса в будущем.) В настоящее время я просто хочу обновить свою таблицу, чтобы она соответствовала последним данным, полученным каждый день. Я работал над процессом сравнения и обновления, но он был очень медленным. Моя таблица базы данных содержит столбцы import_date иified_date, которые в настоящее время заполняются с помощью триггеров. Посредством триггеров каждый оператор INSERT использует current_date как import_date и updated_date для этих записей. Кроме того, для параметра updated_date установлено значение current_date с помощью триггера BEFORE UPDATE. Таким образом, я хочу обновлять только те записи, в которых произошли изменения данных, с последним извлечением данных. В противном случае столбецified_date становится довольно бесполезным, так как я не смогу определить, когда значения для этой записи изменились совсем недавно.

Текущая таблица: ORIG

(фактическая таблица содержит около 1 миллиона записей)

| import_date | Изменено_дата | id | имя | длина | ширина | rp |

| 2018-08-17 | 2018-08-17 | 87 | Синий | 12,0200 | 8,0503 | 1.82 |

| 2018-08-17 | 2018-08-17 | 88 | Красный | 11,0870 | 2,0923 | 1.72 |

| 2018-08-17 | 2018-08-17 | 89 | Розовый | 15,0870 | 7,9963 | 0,95 |

Временная таблица: TEMP

(Также содержит около 1 миллиона записей. Будет содержать все первичные ключи (столбец id), которые существуют в текущей таблице, но могут также содержать новые первичные ключи.)

| import_date | updated_date | id | имя | длина | ширина | rp |

| NULL | NULL | 87 | Чирок | 12,0200 | 8,0503 | 1.82 |

| NULL | NULL | 88 | Красный | 11,0870 | 2,0923 | 1.72 |

| NULL | NULL | 89 | Розовый | 15,0870 | 7,9963 | 0,95 |

Используя данные примера выше, я ожидаю, что будет обновлена ​​только первая запись, id 87. После чего мой стол будет выглядеть так:

| import_date | updated_date | id | имя | длина | ширина | rp |

| 2018-08-17 | 2018-09-12 | 87 | Чирок | 12,0200 | 8,0503 | 1.82 |

| 2018-08-17 | 2018-08-17 | 88 | Красный | 11,0870 | 2,0923 | 1.72 |

| 2018-08-17 | 2018-08-17 | 89 | Розовый | 15,0870 | 7,9963 | 0,95 |

Что сработало для меня: Я обновил свою триггерную функцию modify_date, чтобы определить, когда требуется новая дата изменения:

CREATE FUNCTION my_schema.update_mod_date()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF 
AS $BODY$
DECLARE
BEGIN
    IF tg_op = 'INSERT' THEN
        NEW.modified_date := current_date;
    ELSIF tg_op = 'UPDATE' THEN 
        IF NEW.name IS DISTINCT FROM OLD.name
        OR NEW.length IS DISTINCT FROM OLD.length
        OR NEW.width IS DISTINCT FROM OLD.width
        OR NEW.rp IS DISTINCT FROM OLD.rp THEN
            NEW.modified_date := current_date;
        ELSE
            NEW.modified_date := OLD.modified_date;
        END IF;
    END IF;
    RETURN NEW;
END;
$BODY$;

Тогда я смог использовать оригинальное решение, предложенное @EvanCarroll:

BEGIN;
INSERT INTO my_schema.my_data (SELECT * FROM pg_temp.new_data) 
ON CONFLICT (id) DO UPDATE SET modified_date=NULL, id=EXCLUDED.id,
name=EXCLUDED.name, length=EXCLUDED.length, width=EXCLUDED.width,
rp=EXCLUDED.rp;
COMMIT;

Это гарантировало, что измененная дата изменяется только при изменении одного из других значений в строке.

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

@ EvanCarroll Да, конечная цель - обновить исходную таблицу, используя новый набор данных. - Натан Шайдерер 41 мин. Назад

Тогда ты не хочешь этого делать. Вместо этого вы хотите использовать INSERT ... ON CONFLICT DO UPDATE. Вот как вы внедряетесь в PostgreSQL.

Обновление

Если у вас есть строка, подобная modified_time, которую вы хотите обновить только при обновлении строки, обработайте ее с помощью триггера. Вот так . Тогда вы просто напишите ниже, как это,

INSERT INTO foo
SELECT *
FROM bar
WHERE NOT EXISTS (
  SELECT 1
  FROM foo
  WHERE foo.x = bar.x
    AND NOT foo.whatever = bar.whatever
);

Теперь он не будет принимать обновления в строке, если whatever не отличается для каждого x. В идеале, хотя вы бы этого не сделали. Если строки должны быть уникальными по whatever, я бы добавил это к индексу.

0 голосов
/ 12 сентября 2018

Как насчет присоединения к ПК, но затем выбирать только записи, где остальная часть записи как-то отличается, например, так:

SELECT
    new_data.*
FROM
    my_data
INNER JOIN
    new_data
    ON  (my_data.id = new_data.id) -- Same PK
    AND (ROW(my_data.*) IS DISTINCT FROM ROW(new_data.*)) -- Any difference in other fields

Это вернет записи из таблицы new_data с id, который соответствует записям в my_data, но где другие поля не совпадают.

Документация: https://www.postgresql.org/docs/current/static/functions-comparisons.html#ROW-WISE-COMPARISON

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