【问题标题】:Create unique join id-id matrix if joins parameters is not unique in order of id如果连接参数按 id 的顺序不是唯一的,则创建唯一的连接 id-id 矩阵
【发布时间】: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


【解决方案1】:

你的问题不是很清楚。

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

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

此外,key1 和 key2 都可以是 NULL,因此应将 NULL 值替换为 0 和 "null"。

查询

rn_imported 和 rn_live 是子查询,它们有一个额外的列,其中包含由 ROW_NUMBER() 函数生成的行号。

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

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

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

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

【讨论】:

  • 对不起,如果您不了解所有内容,我会尽量详细说明我想要的内容。但是当我查看您的查询时,它看起来不错,可能是我想要的。我会测试它,如果它可以工作,赏金是你的;)
  • @Perlos 测试查询通常意味着在一些样本数据上尝试它,这些数据尽可能多地涵盖各种有趣的案例。首先,您手动确定此类示例数据的正确结果应该是什么样子,然后运行查询并检查结果是否符合预期。为此,拥有一组良好的样本数据非常重要。如果您能想到一些更有趣的案例应该包含在您的示例数据中,请编辑您的问题并向示例和预期结果添加更多行。因此,如果在某些情况下查询错误,我可以修复查询,或者其他人可以写出更好的答案。
  • 是的,我想改写更简单的问题,这样其他人也可以使用它。
  • 我给你这个赏金,因为我没有时间更改原始查询,萌芽测试工作:)
猜你喜欢
  • 1970-01-01
  • 2017-04-20
  • 1970-01-01
  • 2017-07-18
  • 1970-01-01
  • 2019-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多