我有一张桌子:
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;
这似乎效率很低,并且如果有更多的列,并且在大约10,000个记录的批处理中进行迭代,我看不到在较大的表上有很好的响应时间。
有什么建议可以更有效地识别不同的记录?
更新:
我有一个定期刷新的数据集。不幸的是,我每次都获得完整的数据集,而不仅仅是新的或更新的记录。 (我将在将来修复此过程。)目前,我只想更新表以匹配每天的最新数据。我通过一个过程来处理这些比较和更新,但是速度非常慢。我的数据库表包含import_date和Modifyed_date列,这些列当前正在使用触发器填充。通过触发器,每个INSERT语句都将current_date用作这些记录的import_date和Modifyed_date。此外,通过更新前的触发器将Modifyed_date设置为current_date。因此,我只想更新在最近一次数据提取中实际经历过数据更改的记录。否则,modified_date列将变得毫无用处,因为我将无法确定该记录的值最近的更改时间。
当前表:ORIG
(实际表中包含大约一百万条记录)
| import_date | modified_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
(还包含大约一百万条记录。将包含当前表中存在的所有主键(id列),但也可能包含新的主键。)
| import_date | modified_date | id | 名称 | 长度 | 宽度 | rp |
| NULL | NULL | 87 | Teal | 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 | modified_date | id | 名称 | 长度 | 宽度 | rp |
| 2018-08-17 | 2018-09-12 | 87 | Teal | 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 |
对我有用的东西: 我更新了Modifyed_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;
这确保只有在该行中的其他值之一发生更改时,才会修改Modifyed_date。
答案 0 :(得分:0)
如何加入PK,但是仅选择记录的其余部分有所不同的记录,例如:
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
答案 1 :(得分:0)
@EvanCarroll是的,最终目标是使用新的数据集更新原始表。 – 41分钟前Nathan Scheiderer
然后,您不想这样做。您想改为使用INSERT ... ON CONFLICT DO UPDATE
。这就是您在PostgreSQL中进行upsert的方式。
如果只有 modified_time
这样的行只在更新时才需要更新,请使用触发器对其进行处理。 Like this。然后,您只需像下面这样写,
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
的{{1}}不同,否则它将不接受该行的更新。理想情况下,尽管您不会这样做。如果行必须由x
唯一,则将其添加到索引中。