【发布时间】:2016-07-21 12:29:36
【问题描述】:
问题是在连接键并不总是唯一的情况下连接 2 个具有相似数据的表(将新数据从临时表导入实时表)
在这种情况下,我需要按 id 的顺序解决重复问题(每个 id 应该只有一次,这意味着每个下一个 table1.id 都采取第一个非使用 table2.id ,反之亦然)。
注意:
- 用户可以创建新记录,从而创建更多重复项
- 查询必须在几分之一秒内在 WITH 部分中工作table1 中的数十万条记录(导入数据)和 table2 中的数千条记录(实时数据)
考虑这些数据/表格
|| Imported | || Live |
| Id | guid | key1 | key2 | unimportant | | Id | origGuid | key1 | key2 | important |
| 1 | 1001 | 1 | '01' | 'a' | | 15 | 1001 | 1 | '01' | 'imported' |
| 2 | 1002 | null | '02' | 'b' | | 16 | 1002 | null | '02' | 'imported' |
| 3 | 1003 | null | '02' | 'c' | | 17 | null | null | '02' | 'user restor' |
| 5 | 1005 | 5 | '05' | 'd' | | 18 | 1004 | 4 | '04' | 'imported' |
| 19 | null | null | '02' | 'user new' |
我想得到:
- 从导入的 id 1 记录在现场等于 id 15(是唯一的)
- 从导入的 id 2 记录等于 id 16 在现场
- keys null & '02' 不是唯一的,所以这是第一次出现,并使用相同的键从 live 中获取第一个 id -> 16
- 从导入的 id 3 记录等于在直播中的 id 17
- 与 id 为 2 的行相同的键,这是第二次出现,所以从 live 中获取第二个 id,使用相同的键 ->17(我的意思是第一个未使用)
- 来自导入数据的 id 为 5 的记录是新的
- 实时数据中没有具有相同键的行 -> null
- 在要删除的实时数据标记中记录 ID 为 18,19 的记录
- 导入的数据中没有具有相同键的行 -> null
这里我放了查询准备数据
CREATE TEMPORARY TABLE imported (id serial, guid decimal(30,0), key1 integer, key2 varchar, unimportant varchar);
INSERT INTO imported VALUES (1, 1001, 1, '01', 'a');
INSERT INTO imported VALUES (2, 1002, null, '02', 'b');
INSERT INTO imported VALUES (3, 1003, null, '02', 'c');
INSERT INTO imported VALUES (5, 1005, 5, '05', 'd');
CREATE TEMPORARY TABLE live (id serial, orig_guid integer, key1 integer, key2 varchar, important varchar);
INSERT INTO live VALUES (15, 1001, 1, '01', 'imported');
INSERT INTO live VALUES (16, 1002, null, '02', 'imported');
INSERT INTO live VALUES (17, null, null, '02', 'user restor');
INSERT INTO live VALUES (18, 1004, 4, '04', 'imported');
INSERT INTO live VALUES (19, null, null, '02', 'user new');
我像这样使用旧查询。但它很慢(嵌套循环连接),结果并不完美(未解决重复)
SELECT DISTINCT imported.id AS imported_id, live.id AS live_id
FROM live
INNER JOIN imported ON
live.orig_guid = imported.guid OR (
(live.orig_guid IS NULL OR imported.guid IS NULL) AND
(live.key1 IS NULL AND imported.key1 IS NULL OR live.key1 = imported.key1) AND
(live.key2 IS NULL AND imported.key2 IS NULL OR live.key2 = imported.key2)
)
ORDER BY live.id ASC, imported.id ASC
在优化查询中,我使用 UNION 命令将 SELECT 拆分为 2,并使用 COALESCE 减少 OR 以加快速度
WITH
liveT AS (SELECT id, COALESCE(orig_guid,0) AS guid, COALESCE(key1,0) AS key1, COALESCE(key2,'null') AS key2 FROM live),
importedT AS (SELECT id, COALESCE(guid,0) AS guid, COALESCE(key1,0) AS key1, COALESCE(key2,'null') AS key2 FROM imported),
join1 AS (
SELECT imported.id AS imported_id, live.id AS live_id FROM imported
INNER JOIN live ON imported.guid = live.orig_guid AND imported.guid <> 0 AND live.orig_guid <> 0
),
joins AS (
SELECT imported.id AS imported_id, live.id AS live_id FROM importedT imported
INNER JOIN liveT live ON
(live.guid = 0 OR imported.guid = 0) AND
live.key1 = imported.key1 AND
(live.key2 = imported.key2) -- I have in one key "OR imported.key2 = 'null'" because is new property and is not so strict
-- To reduce records i use AntiJoin
LEFT OUTER JOIN join1 ON join1.imported_id = imported.id
WHERE join1.imported_id IS NULL
UNION
SELECT imported_id, live_id FROM join1
)
SELECT DISTINCT imported_id, live_id FROM joins
ORDER BY imported_id ASC NULLS LAST, live_id ASC NULLS LAST
但结果并不完美,使用3个相似查询
- 这是为了获得连接的(在一个模块中使用 table2 以在另一个模块中使用 table3)
- 第二个我将标记为删除的记录从实时 table3 连接到导入的 table1
- 为了找到删除标记,我使用第一个命令来获取(未)连接
- 需要从导入的表中获取新信息,只是更改了所有者
- 存在于导入的table1和table3中,但由于所有者不匹配而没有转移到table2
- 最后一次在导入的 table1 上下文中搜索 table3 中使用的值
- table3 与新表有关联(id、code、name)
- 导入的 table1 具有属性 code 和 name
- 我需要了解关联表中的哪些记录添加/更新/删除
查询的结果是:
|| Old | || Optimized | || Expected |
import_id | live_id import_id | live_id import_id | live_id
1 | 15 1 | 15 1 | 15
2 | 16 2 | 16 2 | 16
2 | 17 3 | 17 3 | 17
2 | 19 3 | 19 5 | null
3 | 17 null | 18
3 | 19 null | 19
【问题讨论】:
-
我想你在找
distinct on () -
我真的不知道如何使用 distinct 来获得我想要的结果。 Distinct 仅按顺序首次出现。不同的展位值不会产生任何影响。不同的第一个结果 id 2-16, 3-16 矩阵。第二个结果是 2-16、2-17、2-19 矩阵。而这一切都错了
标签: sql postgresql postgresql-9.4