如果连接参数按 id 顺序不唯一,则创建唯一的连接 id-id 矩阵

Create unique join id-id matrix if joins parameters is not unique in order of id

问题是 加入 2 table 具有相似数据的连接键并不总是唯一的(将新数据从临时导入到实时 table)
在这种情况下,我需要 按 id 的顺序解决重复问题(每个 id 应该只有一次 ,意味着接下来每个 table1.id先取未使用的table2.id,反之亦然)。

注:


考虑这些data/tables

|| 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 的记录是新记录
    • 实时数据中没有具有相同键的行 -> 空
  • 实时数据中id为18,19的记录标记为删除
    • 导入的数据中没有具有相同键的行 -> 空


这里我放了查询来准备数据

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');


我像这样使用 old query。但速度很慢(嵌套循环连接),结果也不完美(未解决口是心非)

    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
    • 为了找到删除标记,我使用第一个命令来获得(未)连接
    • 并且需要从导入的 table 中获取新信息,只是更改了所有者
      • 存在于导入的table1table3中,但不转移到table2 因为所有者不匹配
  • 并最后在导入的 table1 上下文中搜索 table3 中使用的值
    • table3 与新的 table (id, code, 姓名)
    • imported table1 有 属性 codename
    • 我需要相关的记录table add/update/delete


查询的结果是:

|| 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

你的问题不是很清楚。

您已将样本数据包含为 INSERT 语句 - 这很好,有助于回答问题。您已经显示了预期的结果——这也很好。通常,如果您用简单的英语解释此结果背后所需的逻辑,它会有所帮助。这部分问题不是很清楚

查看您尝试的查询,我猜测 ImportedLive 表应该在 key1key2 上连接。最重要的是,如果一对 (key1, key2) 不是唯一的,则应按照 id 列定义的顺序逐行连接表。

此外,key1key2都可以是NULL,所以NULL值应该用0"null"代替。

查询

rn_importedrn_live 是具有额外列的子查询,其中包含由 ROW_NUMBER() 函数生成的行号。

然后这些子查询在 key1, key2, rn 上被 FULL 连接在一起。

参见 SQL Fiddle

SELECT
  imported_id
  ,live_id
FROM
  (
    SELECT
      id AS imported_id
      ,COALESCE(key1, 0) AS key1
      ,COALESCE(key2, 'null') AS key2
      ,ROW_NUMBER() OVER (PARTITION BY key1, key2 ORDER BY id) AS rn
    FROM imported
  ) AS rn_imported
  FULL JOIN
  (
    SELECT
      id AS live_id
      ,COALESCE(key1, 0) AS key1
      ,COALESCE(key2, 'null') AS key2
      ,ROW_NUMBER() OVER (PARTITION BY key1, key2 ORDER BY id) AS rn
    FROM live
  ) AS rn_live
  ON rn_imported.key1 = rn_live.key1
  AND rn_imported.key2 = rn_live.key2
  AND rn_imported.rn = rn_live.rn
ORDER BY imported_id ASC NULLS LAST, live_id ASC NULLS LAST

结果

| imported_id | live_id |
|-------------|---------|
|           1 |      15 |
|           2 |      16 |
|           3 |      17 |
|           5 |  (null) |
|      (null) |      18 |
|      (null) |      19 |

为了使这种方法尽可能高效,您应该使 key1key2NOT NULL 以避免调用 COALESCE 函数。函数本身很快,但是这样使用函数通常会导致无法使用索引。删除函数调用的需要后,您应该在两个表中的 (key1, key2, id) 上添加索引。一个索引按此顺序在三列上。让它成为唯一索引不会有什么坏处。它可能会给优化器一些额外的提示。使用此索引 ROW_NUMBER 应该能够生成所需的数字而无需额外排序。拥有两组已排序的数据也应该有助于连接。

我想重复一遍。仅添加索引而不创建列 NOT NULL 很可能是无用的。