У меня есть таблица:
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;
Это гарантировало, что измененная дата изменяется только при изменении одного из других значений в строке.